Example #1
0
def publish(site):
    def get_photo_id(original):
        """Based on an original URL, find the Flickr syndicated URL and
        extract the photo ID

        Returns a tuple with (photo_id, photo_url)
        """
        flickr_url = util.posse_post_discovery(original, FLICKR_PHOTO_RE)
        if flickr_url:
            m = FLICKR_PHOTO_RE.match(flickr_url)
            if m:
                return m.group(2), flickr_url
        return None, None

    def get_path_alias():
        return (site.account.user_info.get('person', {}).get('path_alias') or
                site.account.user_id)

    in_reply_to = request.form.get('in-reply-to')
    like_of = request.form.get('like-of')

    title = request.form.get('name')
    desc = request.form.get('content[value]') or request.form.get('content')

    # try to comment on a photo
    if in_reply_to:
        photo_id, flickr_url = get_photo_id(in_reply_to)
        if not photo_id:
            return util.make_publish_error_response(
                'Could not find Flickr photo to comment on based on URL {}'
                .format(in_reply_to))
        r = call_api_method('POST', 'flickr.photos.comments.addComment', {
            'photo_id': photo_id,
            'comment_text': desc or title,
        }, site=site)
        result = r.json()
        if result.get('stat') == 'fail':
            return util.wrap_silo_error_response(r)
        return util.make_publish_success_response(
            result.get('comment', {}).get('permalink'), result)

    # try to like a photo
    if like_of:
        photo_id, flickr_url = get_photo_id(like_of)
        if not photo_id:
            return util.make_publish_error_response(
                'Could not find Flickr photo to like based on original URL {}'
                .format(like_of))
        r = call_api_method('POST', 'flickr.favorites.add', {
            'photo_id': photo_id,
        }, site=site)
        result = r.json()
        if result.get('stat') == 'fail':
            return util.wrap_silo_error_response(r)
        return util.make_publish_success_response(
            flickr_url + '#liked-by-' + get_path_alias(), result)

    # otherwise we're uploading a photo
    photo_file = util.get_first(util.get_files_or_urls_as_file_storage(request.files, request.form, 'photo'))
    if not photo_file:
        photo_file = util.get_first(util.get_files_or_urls_as_file_storage(request.files, request.form, 'video'))

    if not photo_file:
        return util.make_publish_error_response('Missing "photo" attachment')

    r = upload({'title': title, 'description': desc}, photo_file,
               site=site)

    if r.status_code // 100 != 2:
        return util.wrap_silo_error_response(r)

    photo_id, error = interpret_upload_response(r)
    if error:
        return util.make_publish_error_response(error)

    # maybe add some tags or people
    cats = util.get_possible_array_value(request.form, 'category')
    tags = []
    user_ids = []

    for cat in cats:
        if util.looks_like_a_url(cat):
            resp = call_api_method(
                'GET', 'flickr.urls.lookupUser', {'url': cat}, site=site)
            if resp.status_code // 100 != 2:
                current_app.logger.error(
                    'Error looking up user by url %s. Response: %r, %s',
                    cat, resp, resp.text)
            result = resp.json()
            if result.get('stat') == 'fail':
                current_app.logger.debug(
                    'User not found for url %s', cat)
            else:
                user_id = result.get('user', {}).get('id')
                if user_id:
                    user_ids.append(user_id)
        else:
            tags.append('"' + cat + '"')

    if tags:
        current_app.logger.debug('Adding tags: %s', ','.join(tags))
        resp = call_api_method('POST', 'flickr.photos.addTags', {
            'photo_id': photo_id,
            'tags': ','.join(tags),
        }, site=site)
        current_app.logger.debug('Added tags: %r, %s', resp, resp.text)

    for user_id in user_ids:
        current_app.logger.debug('Tagging user id: %s', user_id)
        resp = call_api_method('POST', 'flickr.photos.people.add', {
            'photo_id': photo_id,
            'user_id': user_id,
        }, site=site)
        current_app.logger.debug('Tagged person: %r, %s', resp, resp.text)

    lat, lng = util.parse_geo_uri(request.form.get('location'))
    if lat and lng:
        current_app.logger.debug('setting location: %s, %s', lat, lng)
        resp = call_api_method('POST', 'flickr.photos.geo.setLocation', {
            'photo_id': photo_id,
            'lat': lat,
            'lon': lng,
        }, site=site)
        current_app.logger.debug('set location: %r, %s', resp, resp.text)

    return util.make_publish_success_response(
        'https://www.flickr.com/photos/{}/{}/'.format(
            get_path_alias(), photo_id))
Example #2
0
def publish(site):
    auth = OAuth1(client_key=current_app.config['TWITTER_CLIENT_KEY'],
                  client_secret=current_app.config['TWITTER_CLIENT_SECRET'],
                  resource_owner_key=site.account.token,
                  resource_owner_secret=site.account.token_secret)

    def interpret_response(result):
        if result.status_code // 100 != 2:
            return util.wrap_silo_error_response(result)

        result_json = result.json()
        twitter_url = 'https://twitter.com/{}/status/{}'.format(
            result_json.get('user', {}).get('screen_name'),
            result_json.get('id_str'))
        return util.make_publish_success_response(twitter_url, result_json)

    def get_tweet_id(original):
        tweet_url = util.posse_post_discovery(original, TWEET_RE)
        if tweet_url:
            m = TWEET_RE.match(tweet_url)
            if m:
                return m.group(1), m.group(2)
        return None, None

    def upload_photo(photo):
        current_app.logger.debug('uploading photo, name=%s, type=%s',
                                 photo.filename, photo.content_type)
        result = requests.post(UPLOAD_MEDIA_URL,
                               files={
                                   'media': (photo.filename, photo.stream,
                                             photo.content_type),
                               },
                               auth=auth)
        if result.status_code // 100 != 2:
            return None, result
        result_data = result.json()
        current_app.logger.debug('upload result: %s', result_data)
        return result_data.get('media_id_string'), None

    def upload_video(video, default_content_type='video/mp4'):
        # chunked video upload
        chunk_files = []

        def cleanup():
            for f in chunk_files:
                os.unlink(f)

        chunk_size = 1 << 20
        total_size = 0
        while True:
            chunk = video.read(chunk_size)
            if not chunk:
                break
            total_size += len(chunk)

            tempfd, tempfn = tempfile.mkstemp(
                '-%03d-%s' % (len(chunk_files), video.filename))
            with open(tempfn, 'wb') as f:
                f.write(chunk)
            chunk_files.append(tempfn)

        current_app.logger.debug('init upload. type=%s, length=%s',
                                 video.content_type, video.content_length)
        result = requests.post(UPLOAD_MEDIA_URL,
                               data={
                                   'command': 'INIT',
                                   'media_type': video.content_type
                                   or default_content_type,
                                   'total_bytes': total_size,
                               },
                               auth=auth)
        current_app.logger.debug('init result: %s %s', result, result.text)
        if result.status_code // 100 != 2:
            cleanup()
            return None, result
        result_data = result.json()
        media_id = result_data.get('media_id_string')
        segment_idx = 0

        for chunk_file in chunk_files:
            current_app.logger.debug('appending file: %s', chunk_file)
            result = requests.post(UPLOAD_MEDIA_URL,
                                   data={
                                       'command': 'APPEND',
                                       'media_id': media_id,
                                       'segment_index': segment_idx,
                                   },
                                   files={
                                       'media': open(chunk_file, 'rb'),
                                   },
                                   auth=auth)
            current_app.logger.debug('append result: %s %s', result,
                                     result.text)
            if result.status_code // 100 != 2:
                cleanup()
                return None, result
            segment_idx += 1

        current_app.logger.debug('finalize uploading video: %s', media_id)
        result = requests.post(UPLOAD_MEDIA_URL,
                               data={
                                   'command': 'FINALIZE',
                                   'media_id': media_id,
                               },
                               auth=auth)
        current_app.logger.debug('finalize result: %s %s', result, result.text)
        if result.status_code // 100 != 2:
            cleanup()
            return None, result
        cleanup()
        return media_id, None

    data = {}
    format = brevity.FORMAT_NOTE
    content = request.form.get('content[value]') or request.form.get('content')

    if 'name' in request.form:
        format = brevity.FORMAT_ARTICLE
        content = request.form.get('name')

    repost_ofs = util.get_possible_array_value(request.form, 'repost-of')
    for repost_of in repost_ofs:
        _, tweet_id = get_tweet_id(repost_of)
        if tweet_id:
            return interpret_response(
                requests.post(RETWEET_STATUS_URL.format(tweet_id), auth=auth))
    else:
        if repost_ofs:
            content = 'Reposted: {}'.format(repost_ofs[0])

    like_ofs = util.get_possible_array_value(request.form, 'like-of')
    for like_of in like_ofs:
        _, tweet_id = get_tweet_id(like_of)
        if tweet_id:
            return interpret_response(
                requests.post(FAVE_STATUS_URL,
                              data={'id': tweet_id},
                              auth=auth))
    else:
        if like_ofs:
            content = 'Liked: {}'.format(like_ofs[0])

    media_ids = []
    for photo in util.get_files_or_urls_as_file_storage(
            request.files, request.form, 'photo'):
        media_id, err = upload_photo(photo)
        if err:
            return util.wrap_silo_error_response(err)
        media_ids.append(media_id)

    for video in util.get_files_or_urls_as_file_storage(
            request.files, request.form, 'video'):
        media_id, err = upload_video(video)
        if err:
            return util.wrap_silo_error_response(err)
        media_ids.append(media_id)

    in_reply_tos = util.get_possible_array_value(request.form, 'in-reply-to')
    for in_reply_to in in_reply_tos:
        twitterer, tweet_id = get_tweet_id(in_reply_to)
        if tweet_id:
            data['in_reply_to_status_id'] = tweet_id
            break
    else:
        if in_reply_tos:
            content = 'Re: {}, {}'.format(in_reply_tos[0], content)

    location = request.form.get('location')
    current_app.logger.debug('received location param: %s', location)
    data['lat'], data['long'] = util.parse_geo_uri(location)

    permalink_url = request.form.get('url')
    if media_ids:
        data['media_ids'] = ','.join(media_ids)

    if content:
        data['status'] = brevity.shorten(content,
                                         permalink=permalink_url,
                                         format=format,
                                         target_length=280)

    # for in-reply-to tweets, leading @mentions will be looked up from the original Tweet, and added to the new Tweet from there.
    # https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-update
    data['auto_populate_reply_metadata'] = 'true'

    data = util.trim_nulls(data)
    current_app.logger.debug('publishing with params %s', data)
    return interpret_response(
        requests.post(CREATE_STATUS_URL, data=data, auth=auth))
Example #3
0
def publish(site):
    title = request.form.get('name')
    content = request.form.get('content[value]') or request.form.get('content')
    permalink = request.form.get('url')

    photo_file = util.get_first(
        util.get_possible_array_value(request.files, 'photo'))
    photo_url = util.get_first(
        util.get_possible_array_value(request.form, 'photo'))

    video_file = util.get_first(
        util.get_possible_array_value(request.files, 'video'))
    video_url = util.get_first(
        util.get_possible_array_value(request.form, 'video'))

    location = request.form.get('location')

    post_data = {'access_token': site.account.token}
    post_files = None
    api_endpoint = 'https://graph.facebook.com/v2.5/me/feed'
    fburl_separator = 'posts'

    message = (content if not permalink else '({})'.format(permalink)
               if not content else '{} ({})'.format(content, permalink))

    if video_file or video_url:
        if video_file:
            post_files = {
                'source': (video_file.filename, video_file.stream,
                           video_file.content_type or 'video/mp4')
            }
        elif video_url:
            post_data['url'] = video_url
        post_data['title'] = title
        post_data['description'] = message
        api_endpoint = 'https://graph-video.facebook.com/v2.5/me/videos'
        fburl_separator = 'videos'
    elif photo_file or photo_url:
        if photo_file:
            post_files = {'source': photo_file}
        elif photo_url:
            post_data['url'] = photo_url
        post_data['caption'] = message
        # TODO support album id as alternative to 'me'
        # TODO upload to "Timeline photos" album by default
        api_endpoint = 'https://graph.facebook.com/v2.5/me/photos'
        fburl_separator = 'photos'
    elif title and content:
        # looks like an article -- include the permalink as a 'link'
        # instead of inline
        post_data['message'] = '{}\n\n{}'.format(title, content)
        post_data['link'] = permalink
        post_data['name'] = title
    elif content:
        post_data['message'] = message
        tokens = brevity.tokenize(content)
        # linkify the first url in the message
        linktok = next((tok for tok in tokens if tok.tag == 'link'), None)
        if linktok:
            post_data['link'] = linktok.content
    else:
        return util.make_publish_error_response(
            'Request must contain a photo, video, or content')

    # posting Location to Facebook is disabled for now -- just
    # searching lat/long does not get us close enough to assume we
    # have the correct place.
    if False and location:
        if location.isnumeric():
            post_data['place'] = location
        else:
            place_name = (request.form.get('place-name')
                          or request.form.get('place_name'))
            lat, lng = util.parse_geo_uri(location)
            if lat and lng:
                current_app.logger.debug('Search FB for a place, %s at %s, %s',
                                         place_name, lat, lng)
                r = requests.get('https://graph.facebook.com/v2.5/search',
                                 params=util.trim_nulls({
                                     'type':
                                     'place',
                                     'center':
                                     '%s,%s' % (lat, lng),
                                     'distance':
                                     '500',
                                     'q':
                                     place_name,
                                     'access_token':
                                     site.account.token,
                                 }))
                if r.status_code != 200:
                    current_app.logger.warning(
                        'FB place search failed with response %r: %r', r,
                        r.text)
                else:
                    places = r.json().get('data', [])
                    if not places:
                        # TODO consider searching without a place name?
                        current_app.logger.warning(
                            'FB no resuts for place %s at %s, %s ', place_name,
                            lat, lng)
                    else:
                        current_app.logger.debug('Found FB place: %s (%s)',
                                                 places[0].get('name'),
                                                 places[0].get('id'))
                        post_data['place'] = places[0].get('id')

    post_data = util.trim_nulls(post_data)
    current_app.logger.debug('Publishing to facebook %s: data=%s, files=%s',
                             api_endpoint, post_data, post_files)
    r = requests.post(api_endpoint, data=post_data, files=post_files)

    # need Web Canvas permissions to do this, which I am too lazy to apply for
    # if r.status_code == 400:
    #     error_data = r.json().get('error', {})
    #     code = error_data.get('code')
    #     subcode = error_data.get('subcode')
    #     # token is expired or otherwise invalid
    #     if code == 190:
    #         send_token_expired_notification(
    #             site.account.user_id,
    #             "silo.pub's Facebook access token has expired. Click the "
    #             "Facebook button on silo.pub's homepage to renew.",
    #             'https://silo.pub/')

    if r.status_code // 100 != 2:
        return util.wrap_silo_error_response(r)

    resp_data = r.json()
    userid = ''
    fbid = resp_data.get('id') or resp_data.get('post_id')

    split = fbid.split('_')
    if len(split) == 2:
        userid, fbid = split

    return util.make_publish_success_response(
        'https://www.facebook.com/{}/{}/{}'.format(
            site.account.username or userid, fburl_separator, fbid),
        data=resp_data)
Example #4
0
def publish(site):
    title = request.form.get('name')
    content = request.form.get('content[value]') or request.form.get('content')
    permalink = request.form.get('url')

    photo_file = util.get_first(util.get_possible_array_value(request.files, 'photo'))
    photo_url = util.get_first(util.get_possible_array_value(request.form, 'photo'))

    video_file = util.get_first(util.get_possible_array_value(request.files, 'video'))
    video_url = util.get_first(util.get_possible_array_value(request.form, 'video'))

    location = request.form.get('location')

    post_data = {'access_token': site.account.token}
    post_files = None
    api_endpoint = 'https://graph.facebook.com/v2.5/me/feed'
    fburl_separator = 'posts'

    message = (
        content if not permalink else
        '({})'.format(permalink) if not content else
        '{} ({})'.format(content, permalink))

    if video_file or video_url:
        if video_file:
            post_files = {'source': (video_file.filename, video_file.stream,
                                     video_file.content_type or 'video/mp4')}
        elif video_url:
            post_data['url'] = video_url
        post_data['title'] = title
        post_data['description'] = message
        api_endpoint = 'https://graph-video.facebook.com/v2.5/me/videos'
        fburl_separator = 'videos'
    elif photo_file or photo_url:
        if photo_file:
            post_files = {'source': photo_file}
        elif photo_url:
            post_data['url'] = photo_url
        post_data['caption'] = message
        # TODO support album id as alternative to 'me'
        # TODO upload to "Timeline photos" album by default
        api_endpoint = 'https://graph.facebook.com/v2.5/me/photos'
        fburl_separator = 'photos'
    elif title and content:
        # looks like an article -- include the permalink as a 'link'
        # instead of inline
        post_data['message'] = '{}\n\n{}'.format(title, content)
        post_data['link'] = permalink
        post_data['name'] = title
    elif content:
        post_data['message'] = message
        tokens = brevity.tokenize(content)
        # linkify the first url in the message
        linktok = next((tok for tok in tokens if tok.tag == 'link'), None)
        if linktok:
            post_data['link'] = linktok.content
    else:
        return util.make_publish_error_response(
            'Request must contain a photo, video, or content')

    # posting Location to Facebook is disabled for now -- just
    # searching lat/long does not get us close enough to assume we
    # have the correct place.
    if False and location:
        if location.isnumeric():
            post_data['place'] = location
        else:
            place_name = (request.form.get('place-name') or
                          request.form.get('place_name'))
            lat, lng = util.parse_geo_uri(location)
            if lat and lng:
                current_app.logger.debug('Search FB for a place, %s at %s, %s',
                                         place_name, lat, lng)
                r = requests.get(
                    'https://graph.facebook.com/v2.5/search',
                    params=util.trim_nulls({
                        'type': 'place',
                        'center': '%s,%s' % (lat, lng),
                        'distance': '500',
                        'q': place_name,
                        'access_token': site.account.token,
                    }))
                if r.status_code != 200:
                    current_app.logger.warning(
                        'FB place search failed with response %r: %r',
                        r, r.text)
                else:
                    places = r.json().get('data', [])
                    if not places:
                        # TODO consider searching without a place name?
                        current_app.logger.warning(
                            'FB no resuts for place %s at %s, %s ',
                            place_name, lat, lng)
                    else:
                        current_app.logger.debug(
                            'Found FB place: %s (%s)', places[0].get('name'),
                            places[0].get('id'))
                        post_data['place'] = places[0].get('id')

    post_data = util.trim_nulls(post_data)
    current_app.logger.debug(
        'Publishing to facebook %s: data=%s, files=%s', api_endpoint,
        post_data, post_files)
    r = requests.post(api_endpoint, data=post_data, files=post_files)

    # need Web Canvas permissions to do this, which I am too lazy to apply for
    # if r.status_code == 400:
    #     error_data = r.json().get('error', {})
    #     code = error_data.get('code')
    #     subcode = error_data.get('subcode')
    #     # token is expired or otherwise invalid
    #     if code == 190:
    #         send_token_expired_notification(
    #             site.account.user_id,
    #             "silo.pub's Facebook access token has expired. Click the "
    #             "Facebook button on silo.pub's homepage to renew.",
    #             'https://silo.pub/')

    if r.status_code // 100 != 2:
        return util.wrap_silo_error_response(r)

    resp_data = r.json()
    userid = ''
    fbid = resp_data.get('id') or resp_data.get('post_id')

    split = fbid.split('_')
    if len(split) == 2:
        userid, fbid = split

    return util.make_publish_success_response(
        'https://www.facebook.com/{}/{}/{}'.format(
            site.account.username or userid, fburl_separator, fbid),
        data=resp_data)
Example #5
0
def publish(site):
    def get_photo_id(original):
        """Based on an original URL, find the Flickr syndicated URL and
        extract the photo ID

        Returns a tuple with (photo_id, photo_url)
        """
        flickr_url = util.posse_post_discovery(original, FLICKR_PHOTO_RE)
        if flickr_url:
            m = FLICKR_PHOTO_RE.match(flickr_url)
            if m:
                return m.group(2), flickr_url
        return None, None

    def get_path_alias():
        return (site.account.user_info.get('person', {}).get('path_alias')
                or site.account.user_id)

    in_reply_to = request.form.get('in-reply-to')
    like_of = request.form.get('like-of')

    title = request.form.get('name')
    desc = request.form.get('content[value]') or request.form.get('content')

    # try to comment on a photo
    if in_reply_to:
        photo_id, flickr_url = get_photo_id(in_reply_to)
        if not photo_id:
            return util.make_publish_error_response(
                'Could not find Flickr photo to comment on based on URL {}'.
                format(in_reply_to))
        r = call_api_method('POST',
                            'flickr.photos.comments.addComment', {
                                'photo_id': photo_id,
                                'comment_text': desc or title,
                            },
                            site=site)
        result = r.json()
        if result.get('stat') == 'fail':
            return util.wrap_silo_error_response(r)
        return util.make_publish_success_response(
            result.get('comment', {}).get('permalink'), result)

    # try to like a photo
    if like_of:
        photo_id, flickr_url = get_photo_id(like_of)
        if not photo_id:
            return util.make_publish_error_response(
                'Could not find Flickr photo to like based on original URL {}'.
                format(like_of))
        r = call_api_method('POST',
                            'flickr.favorites.add', {
                                'photo_id': photo_id,
                            },
                            site=site)
        result = r.json()
        if result.get('stat') == 'fail':
            return util.wrap_silo_error_response(r)
        return util.make_publish_success_response(
            flickr_url + '#liked-by-' + get_path_alias(), result)

    # otherwise we're uploading a photo
    photo_file = util.get_first(
        util.get_files_or_urls_as_file_storage(request.files, request.form,
                                               'photo'))
    if not photo_file:
        photo_file = util.get_first(
            util.get_files_or_urls_as_file_storage(request.files, request.form,
                                                   'video'))

    if not photo_file:
        return util.make_publish_error_response('Missing "photo" attachment')

    r = upload({'title': title, 'description': desc}, photo_file, site=site)

    if r.status_code // 100 != 2:
        return util.wrap_silo_error_response(r)

    photo_id, error = interpret_upload_response(r)
    if error:
        return util.make_publish_error_response(error)

    # maybe add some tags or people
    cats = util.get_possible_array_value(request.form, 'category')
    tags = []
    user_ids = []

    for cat in cats:
        if util.looks_like_a_url(cat):
            resp = call_api_method('GET',
                                   'flickr.urls.lookupUser', {'url': cat},
                                   site=site)
            if resp.status_code // 100 != 2:
                current_app.logger.error(
                    'Error looking up user by url %s. Response: %r, %s', cat,
                    resp, resp.text)
            result = resp.json()
            if result.get('stat') == 'fail':
                current_app.logger.debug('User not found for url %s', cat)
            else:
                user_id = result.get('user', {}).get('id')
                if user_id:
                    user_ids.append(user_id)
        else:
            tags.append('"' + cat + '"')

    if tags:
        current_app.logger.debug('Adding tags: %s', ','.join(tags))
        resp = call_api_method('POST',
                               'flickr.photos.addTags', {
                                   'photo_id': photo_id,
                                   'tags': ','.join(tags),
                               },
                               site=site)
        current_app.logger.debug('Added tags: %r, %s', resp, resp.text)

    for user_id in user_ids:
        current_app.logger.debug('Tagging user id: %s', user_id)
        resp = call_api_method('POST',
                               'flickr.photos.people.add', {
                                   'photo_id': photo_id,
                                   'user_id': user_id,
                               },
                               site=site)
        current_app.logger.debug('Tagged person: %r, %s', resp, resp.text)

    lat, lng = util.parse_geo_uri(request.form.get('location'))
    if lat and lng:
        current_app.logger.debug('setting location: %s, %s', lat, lng)
        resp = call_api_method('POST',
                               'flickr.photos.geo.setLocation', {
                                   'photo_id': photo_id,
                                   'lat': lat,
                                   'lon': lng,
                               },
                               site=site)
        current_app.logger.debug('set location: %r, %s', resp, resp.text)

    return util.make_publish_success_response(
        'https://www.flickr.com/photos/{}/{}/'.format(get_path_alias(),
                                                      photo_id))
Example #6
0
def publish(site):
    auth = OAuth1(
        client_key=current_app.config['TWITTER_CLIENT_KEY'],
        client_secret=current_app.config['TWITTER_CLIENT_SECRET'],
        resource_owner_key=site.account.token,
        resource_owner_secret=site.account.token_secret)

    def interpret_response(result):
        if result.status_code // 100 != 2:
            return util.wrap_silo_error_response(result)

        result_json = result.json()
        twitter_url = 'https://twitter.com/{}/status/{}'.format(
            result_json.get('user', {}).get('screen_name'),
            result_json.get('id_str'))
        return util.make_publish_success_response(twitter_url, result_json)

    def get_tweet_id(original):
        tweet_url = util.posse_post_discovery(original, TWEET_RE)
        if tweet_url:
            m = TWEET_RE.match(tweet_url)
            if m:
                return m.group(1), m.group(2)
        return None, None

    def upload_photo(photo):
        current_app.logger.debug('uploading photo, name=%s, type=%s',
                                 photo.filename, photo.content_type)
        result = requests.post(UPLOAD_MEDIA_URL, files={
            'media': (photo.filename, photo.stream, photo.content_type),
        }, auth=auth)
        if result.status_code // 100 != 2:
            return None, result
        result_data = result.json()
        current_app.logger.debug('upload result: %s', result_data)
        return result_data.get('media_id_string'), None

    def upload_video(video, default_content_type='video/mp4'):
        # chunked video upload
        chunk_files = []

        def cleanup():
            for f in chunk_files:
                os.unlink(f)

        chunk_size = 1 << 20
        total_size = 0
        while True:
            chunk = video.read(chunk_size)
            if not chunk:
                break
            total_size += len(chunk)

            tempfd, tempfn = tempfile.mkstemp('-%03d-%s' % (
                len(chunk_files), video.filename))
            with open(tempfn, 'wb') as f:
                f.write(chunk)
            chunk_files.append(tempfn)

        current_app.logger.debug('init upload. type=%s, length=%s',
                                 video.content_type, video.content_length)
        result = requests.post(UPLOAD_MEDIA_URL, data={
            'command': 'INIT',
            'media_type': video.content_type or default_content_type,
            'total_bytes': total_size,
        }, auth=auth)
        current_app.logger.debug('init result: %s %s', result, result.text)
        if result.status_code // 100 != 2:
            cleanup()
            return None, result
        result_data = result.json()
        media_id = result_data.get('media_id_string')
        segment_idx = 0

        for chunk_file in chunk_files:
            current_app.logger.debug('appending file: %s', chunk_file)
            result = requests.post(UPLOAD_MEDIA_URL, data={
                'command': 'APPEND',
                'media_id': media_id,
                'segment_index': segment_idx,
            }, files={
                'media': open(chunk_file, 'rb'),
            }, auth=auth)
            current_app.logger.debug(
                'append result: %s %s', result, result.text)
            if result.status_code // 100 != 2:
                cleanup()
                return None, result
            segment_idx += 1

        current_app.logger.debug('finalize uploading video: %s', media_id)
        result = requests.post(UPLOAD_MEDIA_URL, data={
            'command': 'FINALIZE',
            'media_id': media_id,
        }, auth=auth)
        current_app.logger.debug('finalize result: %s %s', result, result.text)
        if result.status_code // 100 != 2:
            cleanup()
            return None, result
        cleanup()
        return media_id, None

    data = {}
    format = brevity.FORMAT_NOTE
    content = request.form.get('content[value]') or request.form.get('content')

    if 'name' in request.form:
        format = brevity.FORMAT_ARTICLE
        content = request.form.get('name')

    repost_ofs = util.get_possible_array_value(request.form, 'repost-of')
    for repost_of in repost_ofs:
        _, tweet_id = get_tweet_id(repost_of)
        if tweet_id:
            return interpret_response(
                requests.post(RETWEET_STATUS_URL.format(tweet_id), auth=auth))
    else:
        if repost_ofs:
            content = 'Reposted: {}'.format(repost_ofs[0])

    like_ofs = util.get_possible_array_value(request.form, 'like-of')
    for like_of in like_ofs:
        _, tweet_id = get_tweet_id(like_of)
        if tweet_id:
            return interpret_response(
                requests.post(FAVE_STATUS_URL, data={'id': tweet_id}, auth=auth))
    else:
        if like_ofs:
            content = 'Liked: {}'.format(like_ofs[0])

    media_ids = []
    for photo in util.get_files_or_urls_as_file_storage(request.files, request.form, 'photo'):
        media_id, err = upload_photo(photo)
        if err:
            return util.wrap_silo_error_response(err)
        media_ids.append(media_id)

    for video in util.get_files_or_urls_as_file_storage(request.files, request.form, 'video'):
        media_id, err = upload_video(video)
        if err:
            return util.wrap_silo_error_response(err)
        media_ids.append(media_id)

    in_reply_tos = util.get_possible_array_value(request.form, 'in-reply-to')
    for in_reply_to in in_reply_tos:
        twitterer, tweet_id = get_tweet_id(in_reply_to)
        if tweet_id:
            data['in_reply_to_status_id'] = tweet_id
            break
    else:
        if in_reply_tos:
            content = 'Re: {}, {}'.format(in_reply_tos[0], content)

    location = request.form.get('location')
    current_app.logger.debug('received location param: %s', location)
    data['lat'], data['long'] = util.parse_geo_uri(location)

    permalink_url = request.form.get('url')
    if media_ids:
        data['media_ids'] = ','.join(media_ids)

    if content:
        data['status'] = brevity.shorten(content, permalink=permalink_url,
                                         format=format, target_length=280)

    # for in-reply-to tweets, leading @mentions will be looked up from the original Tweet, and added to the new Tweet from there.
    # https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-update
    data['auto_populate_reply_metadata'] = 'true'

    data = util.trim_nulls(data)
    current_app.logger.debug('publishing with params %s', data)
    return interpret_response(
        requests.post(CREATE_STATUS_URL, data=data, auth=auth))