Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/heroku/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python oauth2client,用于从Heroku服务器中的电报机器人发布博客_Python_Heroku_Google Api_Google Oauth_Oauth2client - Fatal编程技术网

Python oauth2client,用于从Heroku服务器中的电报机器人发布博客

Python oauth2client,用于从Heroku服务器中的电报机器人发布博客,python,heroku,google-api,google-oauth,oauth2client,Python,Heroku,Google Api,Google Oauth,Oauth2client,我刚刚在Heroku部署了带有python电报机器人的电报机器人 我的webhooks机器人使用blogger发布某些内容。到目前为止,我都是通过谷歌apli_客户端模块对样例_工具的定制版本稍加修改来实现这一点的 我的工具: """ dependencies: pip3 install --upgrade google-api-python-client This is a slightly modified implementation for substituting goog

我刚刚在Heroku部署了带有python电报机器人的电报机器人

我的webhooks机器人使用blogger发布某些内容。到目前为止,我都是通过谷歌apli_客户端模块对样例_工具的定制版本稍加修改来实现这一点的

我的工具

"""
dependencies:
    pip3 install --upgrade google-api-python-client

This is a slightly modified implementation 
for substituting googleapiclient.sample_tools. It helps customizing some paths 
for my project files under different environments
"""
from __future__ import absolute_import

from environments import get_active_env

__all__ = ['init']

import argparse
import os

from googleapiclient import discovery
from googleapiclient.http import build_http
from oauth2client import tools, file, client


def init(argv, name, version, doc, scope=None, parents=[], 
         discovery_filename=None):
    """A common initialization routine for samples.

    Many of the sample applications do the same initialization, which has now
    been consolidated into this function. This function uses common idioms found
    in almost all the samples, i.e. for an API with name 'apiname', the
    credentials are stored in a file named apiname.dat, and the
    client_secrets.json file is stored in the same directory as the application
    main file.

    Args:
        argv: list of string, the command-line parameters of the application.
        name: string, name of the API.
        version: string, version of the API.
        doc: string, description of the application. Usually set to __doc__.
        file: string, filename of the application. Usually set to __file__.
        parents: list of argparse.ArgumentParser, additional command-line flags.
        scope: string, The OAuth scope used.
        discovery_filename: string, name of local discovery file (JSON). Use 
        when discovery doc not available via URL.

    Returns:
    A tuple of (service, flags), where service is the service object and flags
    is the parsed command-line flags.
    """
    if scope is None:
        scope = 'https://www.googleapis.com/auth/' + name

    # Parser command-line arguments.
    parent_parsers = [tools.argparser]
    parent_parsers.extend(parents)
    parser = argparse.ArgumentParser(
        description=doc,
        formatter_class=argparse.RawDescriptionHelpFormatter,
        parents=parent_parsers)
    flags = parser.parse_args(argv[1:])

    # Name of a file containing the OAuth 2.0 information for this
    # application, including client_id and client_secret, which are found
    # on the API Access tab on the Google APIs
    # Console <http://code.google.com/apis/console>.
    client_secrets = os.path.join(os.path.dirname(__file__), get_active_env(),
                                 'client_secrets.json')

    # Set up a Flow object to be used if we need to authenticate.
    flow = client.flow_from_clientsecrets(client_secrets,
      scope=scope,
      message=tools.message_if_missing(client_secrets))

    # Prepare credentials, and authorize HTTP object with them.
    # If the credentials don't exist or are invalid, 
    # run through the native client flow.
    # The Storage object will ensure that if successful the good
    # credentials will get written back to a file in google_core directory.
    storage_file_path = os.path.join(os.path.dirname(__file__), name + '.dat')
    storage = file.Storage(storage_file_path)
    credentials = storage.get()
    if credentials is None or credentials.invalid:
        credentials = tools.run_flow(flow, storage, flags)
    http = credentials.authorize(http=build_http())

    if discovery_filename is None:
        # Construct a service object via the discovery service.
        service = discovery.build(name, 
                                  version, 
                                  http=http, 
                                  cache_discovery=False)
    else:
        # Construct a service object using a local discovery document file.
        with open(discovery_filename) as discovery_file:
            service = discovery.build_from_document(
                discovery_file.read(),
                base='https://www.googleapis.com/',
                http=http)
        service = discovery.build(name, 
                                  version, 
                                  http=http, 
                                  cache_discovery=False)
    return (service, flags)
service, flags = my_tools.init(
    [], 'blogger', 'v3', __doc__,
    scope='https://www.googleapis.com/auth/blogger')

try:
    posts = service.posts()
    # This new_post is a custom object, but the important thing here
    # is getting the authorization, and then the service at the top
    insert = posts.insert(blogId=new_post.blog_id, body=new_post.body(), isDraft=new_post.is_draft)
    posts_doc = insert.execute()
    return posts_doc
except client.AccessTokenRefreshError:
    print('The credentials have been revoked or expired, please re-run the application to re-authorize')
from oauth2client import service_account
    import googleapiclient.discovery
    import os
    from environments import get_active_env
    SERVICE_ACCOUNT_FILE = os.path.join(os.path.dirname(__file__), os.pardir, 'google_core', get_active_env(),
                                        'service_account.json')

    credentials = service_account.ServiceAccountCredentials.from_json_keyfile_name(
        SERVICE_ACCOUNT_FILE, scopes=['https://www.googleapis.com/auth/blogger'])
    service = googleapiclient.discovery.build('blogger', 'v3', credentials=credentials)

    try:
        posts = service.posts()
        insert = posts.insert(blogId=new_post.blog_id, body=new_post.body(), isDraft=new_post.is_draft)
        posts_doc = insert.execute()
        return posts_doc
    except client.AccessTokenRefreshError:
        print('The credentials have been revoked or expired, please re-run the application to re-authorize')
但现在我不能这样做,因为它在heroku中,日志中会显示以下消息:

app[web.1]: Your browser has been opened to visit:
app[web.1]: 
app[web.1]:     https://accounts.google.com/o/oauth2/auth?client_id=<client_id>&redirect_uri=http%3A%2F%2Flocalhost%3A8090%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fblogger&access_type=offline&response_type=code
app[web.1]: 
app[web.1]: If your browser is on a different machine then exit and re-run this
app[web.1]: application with the command-line parameter
app[web.1]: 
app[web.1]:   --noauth_local_webserver
app[web.1]:
因此,我现在在日志中看到了这一点(我认为403 HttpError就是问题所在,关于memcache或oauth2client.contrib.locked_文件未导入的另一个错误不是什么大问题):

heroku[web.1]:unilling heroku[web.1]:状态从“向下”更改为“开始” heroku[web.1]:使用命令“python my_bot.py”启动进程` heroku[web.1]:状态从启动更改为启动 heroku[路由器]:at=info method=POST path=“/”host=telegram-bot-alfred.herokuapp.com请求_id=fwd=“”dyno=web.1 connect=1ms服务=2ms状态=200字节=97协议=https 应用程序[web.1]:信息-输入:post\u asin 应用程序[web.1]:信息-输入ASIN:B079Z8THTF 应用程序[web.1]:信息-asin B079Z8THTF的打印优惠: 应用[网站1]:信息-36.98欧元 应用程序[web.1]:信息-请求的URL:获取https://www.googleapis.com/discovery/v1/apis/blogger/v3/rest 应用程序[web.1]:信息-尝试刷新以获取初始访问权\u令牌 app[web.1]:信息-请求的URL:POSThttps://www.googleapis.com/blogger/v3/blogs/2270688467086771731/posts?isDraft=true&alt=json 应用程序[web.1]:信息-刷新访问令牌 应用程序[web.1]:警告-遇到403禁止,原因为“禁止” 应用程序[web.1]:错误-asin B079Z8THTF错误。我们去下一个。 应用程序[web.1]:回溯(最近一次通话): app[web.1]:文件“my_bot.py”,第171行,进程中字符串 应用程序[web.1]:向博客作者发送帖子(update.message,post) app[web.1]:文件“/app/api\u samples/blogger/blogger\u insert.py”,第85行,发送给blogger app[web.1]:response=post\u at\u blogger(post) app[web.1]:文件“/app/api\u samples/blogger/blogger\u insert.py”,第72行,在blogger的post\u中 app[web.1]:posts\u doc=insert.execute() app[web.1]:文件“/app/.heroku/python/lib/python3.6/site packages/googleapiclient/http.py”,执行中的第844行 app[web.1]:引发HttpError(resp,content,uri=self.uri) app[web.1]:文件“/app/.heroku/python/lib/python3.6/site packages/oauth2client/\u helpers.py”,第133行,位置包装中 应用程序[web.1]:返回包装(*args,**kwargs) 应用程序[web.1]:GoogleAppClient.errors.HttpError: 应用程序[web.1]:错误-未处理异常HttpError 应用程序[web.1]:回溯(最近一次通话): app[web.1]:文件“my_bot.py”,第171行,进程中字符串 应用程序[web.1]:向博客作者发送帖子(update.message,post) app[web.1]:文件“/app/api\u samples/blogger/blogger\u insert.py”,第85行,发送给blogger app[web.1]:response=post\u at\u blogger(post) app[web.1]:文件“/app/api\u samples/blogger/blogger\u insert.py”,第72行,在blogger的post\u中 app[web.1]:posts\u doc=insert.execute() app[web.1]:文件“/app/.heroku/python/lib/python3.6/site packages/googleapiclient/http.py”,执行中的第844行 app[web.1]:引发HttpError(resp,content,uri=self.uri) app[web.1]:文件“/app/.heroku/python/lib/python3.6/site packages/oauth2client/\u helpers.py”,第133行,位置包装中 应用程序[web.1]:返回包装(*args,**kwargs) 应用程序[web.1]:GoogleAppClient.errors.HttpError: 应用程序[web.1]: app[web.1]:在处理上述异常时,发生了另一个异常: 应用程序[web.1]: 应用程序[web.1]:回溯(最近一次通话): app[web.1]:文件“/app/exceptions/errors.py”,第47行,在alfred中 应用程序[web.1]:消息.回复\文本(rnd.choice(回答[类型(例外)]) 应用程序[web.1]:键错误: 应用程序[web.1]:警告-asin B079Z8THTF错误。我们去下一个
我通过提供如下参数找到了解决方案:

service, flags = my_tools.init(
    ['', '--noauth_local_webserver'], 'blogger', 'v3', __doc__,
    scope='https://www.googleapis.com/auth/blogger')
然后我不得不从oauth2client.tools定制一些方法。我在我的工具中使用了两种方法和附加代码。丢失的每一件物品都可以很容易地从谷歌的原始工具中导入或复制:

# module scope
import argparse
from googleapiclient import discovery
from googleapiclient.http import build_http
from oauth2client import tools, file, client, _helpers
from oauth2client.tools import _CreateArgumentParser

_GO_TO_LINK_MESSAGE = """
Visit this link to get auth code

    {address}

"""

# argparser is an ArgumentParser that contains command-line options expected
# by tools.run(). Pass it in as part of the 'parents' argument to your own
# ArgumentParser.
argparser = _CreateArgumentParser()

_flow = None


# Methods
@_helpers.positional(3)
def run_flow(flow, flags=None):
    """
    Emulates the original method run_flow from oauth2client.tools getting the website to visit.

    The ``run()`` function is called from your application and runs
    through all the steps to obtain credentials. It takes a ``Flow``
    argument and attempts to open an authorization server page in the
    user's default web browser. The server asks the user to grant your
    application access to the user's data.  The user can then get an
    authentication code for inputing later

    :param flow: the google OAuth 2.0 Flow object with which the auth begun
    :param flags: the provided flags
    :return: the string with the website link where the user can authenticate and obtain a code
    """
    global _flow

    # I update the _flow object for using internally later
    _flow = flow

    # Really the flags aren't very used. In practice I copied the method as if noauth_local_webserver was provided
    if flags is None:
        flags = argparser.parse_args()
    logging.getLogger().setLevel(getattr(logging, flags.logging_level))

    oauth_callback = client.OOB_CALLBACK_URN
    _flow.redirect_uri = oauth_callback
    authorize_url = _flow.step1_get_authorize_url()

    return _GO_TO_LINK_MESSAGE.format(address=authorize_url)


def oauth_with(code, http=None):
    """
    If the code grants access,
    the function returns new credentials. The new credentials
    are also stored in the ``storage`` argument, which updates the file
    associated with the ``Storage`` object.

    :param code: the auth code
    :param http: the http transport object
    :return: the credentials if any
    """
    global _flow
    storage_file_path = get_credentials_path('blogger')
    storage = file.Storage(storage_file_path)
    try:
        # We now re-use the _flow stored earlier
        credential = _flow.step2_exchange(code, http=http)
    except client.FlowExchangeError as e:
        raise AlfredException(msg='Authentication has failed: {0}'.format(e))

    storage.put(credential)
    credential.set_store(storage)
    # We reset the flow
    _flow = None

    return credential
# module scope
import argparse
from googleapiclient import discovery
from googleapiclient.http import build_http
from oauth2client import tools, file, client, _helpers
from oauth2client.tools import _CreateArgumentParser

_GO_TO_LINK_MESSAGE = """
Visit this link to get auth code

    {address}

"""

# argparser is an ArgumentParser that contains command-line options expected
# by tools.run(). Pass it in as part of the 'parents' argument to your own
# ArgumentParser.
argparser = _CreateArgumentParser()

_flow = None


# Methods
@_helpers.positional(3)
def run_flow(flow, flags=None):
    """
    Emulates the original method run_flow from oauth2client.tools getting the website to visit.

    The ``run()`` function is called from your application and runs
    through all the steps to obtain credentials. It takes a ``Flow``
    argument and attempts to open an authorization server page in the
    user's default web browser. The server asks the user to grant your
    application access to the user's data.  The user can then get an
    authentication code for inputing later

    :param flow: the google OAuth 2.0 Flow object with which the auth begun
    :param flags: the provided flags
    :return: the string with the website link where the user can authenticate and obtain a code
    """
    global _flow

    # I update the _flow object for using internally later
    _flow = flow

    # Really the flags aren't very used. In practice I copied the method as if noauth_local_webserver was provided
    if flags is None:
        flags = argparser.parse_args()
    logging.getLogger().setLevel(getattr(logging, flags.logging_level))

    oauth_callback = client.OOB_CALLBACK_URN
    _flow.redirect_uri = oauth_callback
    authorize_url = _flow.step1_get_authorize_url()

    return _GO_TO_LINK_MESSAGE.format(address=authorize_url)


def oauth_with(code, http=None):
    """
    If the code grants access,
    the function returns new credentials. The new credentials
    are also stored in the ``storage`` argument, which updates the file
    associated with the ``Storage`` object.

    :param code: the auth code
    :param http: the http transport object
    :return: the credentials if any
    """
    global _flow
    storage_file_path = get_credentials_path('blogger')
    storage = file.Storage(storage_file_path)
    try:
        # We now re-use the _flow stored earlier
        credential = _flow.step2_exchange(code, http=http)
    except client.FlowExchangeError as e:
        raise AlfredException(msg='Authentication has failed: {0}'.format(e))

    storage.put(credential)
    credential.set_store(storage)
    # We reset the flow
    _flow = None

    return credential