You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何将Gmail API授权码传递至Heroku上的Node.js Discord Bot进程

解决Heroku上Discord Bot的Gmail API授权问题

你的问题核心在于Heroku的部署环境不支持交互式的stdin输入,而且它的文件系统是临时的——即使你成功输入授权码保存了token,dyno重启后文件也会丢失。下面给你几个可行的解决方案,按推荐程度排序:

方案1:本地预生成Token,用Heroku环境变量存储(最简便)

这是最适合你的场景的方法,因为你只需要一次授权,之后靠refresh token自动续期:

  1. 本地生成有效Token
    在本地运行你的Bot,完成授权流程,生成token.json文件(对应你代码里的TOKEN_PATH)。这个文件里包含了access_tokenrefresh_token,后者可以长期用来刷新令牌。

  2. 将Token存入Heroku环境变量

    • 打开Heroku控制台,找到你的应用,进入「Settings」→「Config Vars」
    • 添加一个新的环境变量,比如命名为GMAIL_TOKEN,值就是token.json里的完整JSON字符串(直接复制文件里的内容即可)
  3. 修改代码读取环境变量
    替换原来读取本地文件和保存文件的逻辑,因为Heroku的临时文件系统无法持久化保存token:

    function authorize(credentials, callback) {
      const {client_secret, client_id, redirect_uris} = credentials.installed;
      const oAuth2Client = new google.auth.OAuth2(
        client_id, client_secret, redirect_uris[0]);
    
      // 从环境变量读取token
      const token = JSON.parse(process.env.GMAIL_TOKEN);
      oAuth2Client.setCredentials(token);
    
      // googleapis库会自动处理token刷新,无需手动保存
      oAuth2Client.on('tokens', (tokens) => {
        // 除非refresh_token更新(极少发生),否则无需额外操作
      });
    
      callback(oAuth2Client);
    }
    
    // 移除原来的Fs.writeFile逻辑,环境变量已实现持久化存储
    

    这样你的Bot启动时会直接从环境变量加载token,不需要再交互式输入授权码。

方案2:使用服务账号认证(适合G Suite/Workspace用户)

如果你的Bot需要访问的是你自己的Gmail账号,且你使用的是Google Workspace(原G Suite),可以用服务账号跳过授权码流程:

  1. 创建服务账号
    在Google Cloud Console中,找到你的项目→「IAM与管理」→「服务账号」,创建一个新的服务账号,下载JSON密钥文件。

  2. 授权服务账号访问你的邮箱
    在Workspace管理控制台中,给这个服务账号授权「Gmail API权限」,并允许它访问你的个人邮箱(需要管理员权限)。

  3. 将密钥存入Heroku环境变量
    把服务账号JSON文件的内容作为环境变量GMAIL_SERVICE_ACCOUNT_KEY添加到Heroku。

  4. 修改代码使用服务账号认证

    const { google } = require('googleapis');
    
    const serviceAccount = JSON.parse(process.env.GMAIL_SERVICE_ACCOUNT_KEY);
    const auth = new google.auth.JWT(
      serviceAccount.client_email,
      null,
      serviceAccount.private_key,
      ['https://www.googleapis.com/auth/gmail.readonly'], // 替换成你需要的权限
      'your-email@example.com' // 你要访问的邮箱地址
    );
    
    // 初始化Gmail客户端
    const gmail = google.gmail({ version: 'v1', auth });
    
    // 之后直接用gmail对象调用API即可
    

    注意:个人Gmail账号无法使用服务账号直接访问,必须是Workspace账号才行。

方案3:临时启动HTTP服务器接收授权码(适合一次性授权)

如果不想用环境变量,也可以临时启动一个简单的HTTP服务器,让Google把授权码回调到Heroku的URL:

  1. 修改授权URL的回调地址
    在Google Cloud Console中,把OAuth2的回调URL改成你的Heroku应用URL,比如https://your-heroku-app.herokuapp.com/auth/callback

  2. 添加HTTP服务器代码
    用Express快速搭建一个临时服务器:

    const express = require('express');
    const app = express();
    const PORT = process.env.PORT || 3000;
    
    // 处理授权回调
    app.get('/auth/callback', (req, res) => {
      const code = req.query.code;
      // 用这个code获取token,和你原来的getNewToken逻辑一致
      oAuth2Client.getToken(code, (err, token) => {
        if (err) {
          res.send('Error getting token');
          return console.error(err);
        }
        // 把token打印到控制台,之后你可以复制出来存到环境变量
        console.log('Token:', JSON.stringify(token));
        res.send('Authorization successful! You can close this page now.');
        // 完成授权后关闭服务器,继续运行Bot
        process.exit(0);
      });
    });
    
    app.listen(PORT, () => {
      console.log('Server running on port', PORT);
      const authUrl = oAuth2Client.generateAuthUrl({
        access_type: "offline",
        scope: SCOPES,
      });
      console.log('Authorize via:', authUrl);
    });
    

    部署到Heroku后,打开控制台查看日志里的授权URL,访问它完成授权,控制台会输出token,之后你把这个token存到环境变量,再改用方案1的方式加载即可。

为什么之前的方法失败?

  • Heroku的dyno进程启动后,stdin并没有和你的CLI会话关联,所以你通过Heroku CLI或Dynamo输入的内容无法被readline捕获。
  • Heroku的文件系统是临时的,即使你成功保存了token文件,dyno重启后文件会被清空,所以必须用持久化的存储方式(比如环境变量)。

内容的提问来源于stack exchange,提问作者Gh05d

火山引擎 最新活动