几年前曾看到过@iBug大佬的一个GitHub仓库,这个仓库的名字以及描述会实时显示当前的Star数量,令当时的我觉得非常有意思,不过那会我并没有去研究原理。几年过去了,却不知昨天为啥突然想到,于是读了一下大佬的文章,试着复现了一下。

我复现的仓库链接如下:

朋友们可以进去点个star看看效果~(没想到我第一次骗star竟是为这)


项目很简单,但其背后的原理覆盖了一些我之前没有接触过的东西,例如GitHub REST API、Webhook(这个倒是在做telegram bot的时候接触过)、AWS Lambda等。如果能熟练利用,感觉会是个很高效、很方便的辅助工具。由于iBug大佬的文章省略了好多「有手就行」但我不会的内容,所以在过程中踩了一些坑,因此我来水篇文章,更详细地记录一下。

实现原理

这个东西的原理其实就是,使用GitHub自带的Webhook,监听仓库的各种事件(如本例监听的是「Star」事件),一旦事件发生,则自动推送消息到指定的URL,通过URL背后的代码来调用GitHub REST API,从而实现在Star数更新时自动修改GitHub repo名。我们可以自定义该URL,为降低复杂度、提升维护的便利性,选择使用AWS Lambda这种Serverless的服务。

创建GitHub Repo并添加Webhook

第一步自然是需要把仓库建立起来,可以随便起个名,例如“This-Repo-Has-0-Stars”。然后前往仓库的「Settings->Webhooks」标签,点击「Add webhook」。

  • Payload URL:推送消息的目的地址,这里我们还没有申请AWS的API,先不管。
  • Content type:我比较喜欢用JSON,于是我选择了「application/json」,实际也就是写代码的时候略有区别。
  • Secret:Webhook的签名字符串,可用来验签,可以不填,不过还是建议用密码生成器来生成一个。
  • Which events would you like to trigger this webhook? 由于我们的目的是监听Star事件,因此这里选择「Let me select individual events」,然后在下面的列表中取消勾选「Pushes」,勾选「Stars」。

于是还剩Payload URL没有配置,暂时先放着,进入下一步。

创建GitHub Access Token

为通过GitHub REST API修改仓库名,需要搞一个token用来鉴权。此处前往token申请页面


如上,申请一个用于管理repo的token,将其保存下来。

创建AWS Lambda函数

由于之前发生过一次因不了解AWS平台的计费方式而白白浪费了100多rmb的惨痛经历,于是这次我提前摸清了套路:


作为一个娱乐项目肯定稳稳够用了。

前往AWS控制台,在「服务」中搜索lambda:


创建一个lambda函数,并选择你喜欢的编程语言(Python 3.8)


点「创建函数」即可。进入代码页,发现它为我们提供了最基础的(Python)代码:

import json

def lambda_handler(event, context):
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

不过它好像并不能直接使用,而是要通过AWS其他地方的接口来调用,这个东西称为AWS API Gateway

配置AWS API Gateway


这玩意则是收费的,不过很便宜,不公开接口的话应该开销不大。

点击「创建API」,并选择「HTTP API」:


添加一个Lambda集成,并选择前面创建的Lambda函数,最后点「下一步」:


配置路由,按iBug大佬所说,填写$default:


这样便创建好了一个HTTP API,AWS为其分配了一个ID,即下面的87z7tufbtk:


可以通过下面的方法简单测试一下:

$ curl https://87z7tufbtk.execute-api.us-east-1.amazonaws.com/
"Hello from Lambda!"%

这串URL:https://87z7tufbtk.execute-api.us-east-1.amazonaws.com/ 也正是前面创建webhook时留着没填的那个Payload URL。

编写Lambda函数

刚刚的接口测试说明Lambda函数已经work了,于是需要编写一下这个Lambda函数以使其达到我们想要的效果——获取到repo的star数,然后把它的名字、描述改了。这里的参数研究等过程建议直接阅读iBug大佬的文章,我就直接贴完整的代码了(此代码修改自iBug大佬的代码,也可以在我的仓库里看到)

代码

import base64
import hashlib
import hmac
import os
import json

import requests


GITHUB_TOKEN = os.environ.get('GITHUB_TOKEN')
SECRET = os.environ.get('SECRET')


def lambda_handler(event, context):
    path = event['rawPath']
    if 'body' in event:
        if event['isBase64Encoded']:
            body = base64.b64decode(event['body'])
        else:
            body = event['body'].encode()
    else:
        body = b""
    signature = event['headers']['x-hub-signature-256'].split("=")[1]
    hashsum = hmac.new(SECRET.encode(), msg=body, digestmod=hashlib.sha256).hexdigest()
    if hashsum != signature:
        return {
            'statusCode': 401,
            'body': 'Bad signature'
        }
    if path == "/":
        # https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads#star
        payload = json.loads(body)
        repository = payload['repository']
        repository_url = repository['url']
        stars = repository['stargazers_count']

        new_name = f"This-Repo-Has-{stars}-Star{'s' if stars != 1 else ''}"
        new_description = f"Thanks for stopping by! This repository now has {stars} star{'s' if stars != 1 else ''}~🌟🌟🌟"
        headers = {'Authorization': f"Bearer {GITHUB_TOKEN}"}
        data = {
            'name': new_name,
            'description': new_description
        }
        response = requests.patch(repository_url, headers=headers, json=data)
        return {'statusCode': response.status_code, 'body': "OK"}
    else:
        return {'statusCode': 404, 'body': ""}

配置环境变量

注意到代码最前面读取了俩环境变量,GITHUB_TOKEN和SECRET分别是前面申请的GitHub Access Token和Webhook Secret字符串,为在代码中获取,需要将其设置在Lambda函数的「配置->环境变量」标签栏下:

添加第三方库

设置完上面的内容后,我给仓库点了个star,然而


debug了半天发现原来是Lambda函数的Python 3.8环境没有第三方库,例如requests,于是要想办法把第三方库集成进去。

研究了一会,发现Lambda提供一种叫「层」的功能:


通过添加「层」,可以将第三方库打包添加进去。下面先打包第三方库。

在本地的Python 3.8环境中执行下述命令,得到python目录,将该目录打包为python.zip

pip install --target ./python requests urllib3==1.26.16

然后「创建层」:


名称可以随意填写,点击「上传」按钮,将前面的python.zip上传,选择几个合适的运行时,点击「创建」。

回到Lambda的「代码」部分,拉到最底下,有一块「层」区域:


我们在这里添加刚刚创建的层,即可让Python环境拥有requests。


最后

到了这里,这个会自动随star数更新名字和描述的repo就已经做完了。不过其中所用到的技术,仍有很大的应用空间尚未研究,「下次一定」找个机会多玩玩!