From bf439e97662dd5f050dd2684531c0052814c98f5 Mon Sep 17 00:00:00 2001 From: zhgchgli Date: Tue, 1 Aug 2023 22:52:24 +0800 Subject: [PATCH] update en --- _posts/en/2023-08-01-382218e15697_en.md | 461 ++++++++++++++++++ .../2023-08-01-382218e15697.md | 4 +- 2 files changed, 463 insertions(+), 2 deletions(-) create mode 100644 _posts/en/2023-08-01-382218e15697_en.md diff --git a/_posts/en/2023-08-01-382218e15697_en.md b/_posts/en/2023-08-01-382218e15697_en.md new file mode 100644 index 000000000..8328191cd --- /dev/null +++ b/_posts/en/2023-08-01-382218e15697_en.md @@ -0,0 +1,461 @@ +--- +title: "Create a Github Repo Star Notifier for Free with Google Apps Script in Three Simple Steps" +author: "ZhgChgLi" +date: 2023-08-01T21:32:14.687+0000 +last_modified_at: 2023-08-01T21:32:14.687+0000 +categories: "ZRealm Dev." +tags: ["ios-app-development","google-app-script","github","notifications","stars"] +description: "Learn how to use Google Apps Script to integrate Github Webhook and forward star (like) notifications to SNS(Line)." +image: + path: /assets/382218e15697/1*fUtW942huwnSbPF-ar4OCQ.png +render_with_liquid: false +--- + +### How to Build a Github Repo Star Notifier for Free in Three Simple Steps Using Google Apps Script + +Writing a GAS script to integrate with Github Webhook and forward star (like) notifications to Line. + +#### Introduction + +As maintainers of open-source projects, we are not driven by money or fame, but by a sense of **vanity**. Every time we see a new ⭐️ star, it brings immense joy to our hearts. Knowing that the projects we invest our time and energy into are being used and actually helping friends with similar problems is truly rewarding. + +![Star History Chart](/assets/382218e15697/1*fUtW942huwnSbPF-ar4OCQ.png) + +[Star History Chart](https://star-history.com/#ZhgChgLi/ZMarkupParser&Date){:target="_blank"} + +Because of this, I tend to have a slight obsession with ⭐️ stars. I find myself frequently checking Github to see if the ⭐️ star count has increased. I wondered if there could be a more proactive way to receive notifications when someone hits that ⭐️ star, without having to manually track and search for it. + +#### Existing Tools + +Firstly, let's consider looking for existing tools to achieve this. I searched on [Github Marketplace](https://github.com/marketplace?type=apps&query=Star+Notifier+) and found several awesome tools that are available. + +![](/assets/382218e15697/1*wQZ6-F77v2SEepm90YAF-A.png) + +I tried some of them, but the results were not as expected. Some of them are no longer operational, while others can only send notifications when reaching every 5/10/20 ⭐️ stars (I'm just a small user, so getting 1 ⭐️ already makes me happy 😝). Additionally, these tools only offer email notifications, but I prefer using SNS notifications. + +Moreover, I feel a bit uneasy about installing an app just for the sake of "vanity," as I'm concerned about potential cybersecurity risks. + +On iOS, both the [Github](https://apps.apple.com/tw/app/github/id1477376905) app and [GitTrends](https://github.com/brminnick/GitTrends) and other third-party apps do not support this feature. + +### Building Your Own Github Repo Star Notifier + +Based on the above, we can actually use Google Apps Script for free and quickly create our own Github Repo Star Notifier. + +#### Preparation + +In this article, we'll use Line as the notification medium. If you want to use other communication/SNS software for notification, you can ask [ChatGPT](https://chat.openai.com){:target="_blank"} how to implement it. + +![Ask [ChatGPT](https://chat.openai.com){:target="_blank"} how to implement Line Notify](https://assets.example.com/1*NzEyi3zdzD5QDhLvpsFocA.png) + +Ask [ChatGPT](https://chat.openai.com){:target="_blank"} how to implement Line Notify + +`lineToken`: + +- Go to [Line Notify](https://notify-bot.line.me/my/){:target="_blank"} + +- After logging in to your Line account, scroll down to find the "Generate access token (For developers)" section. + +![Generate access token (For developers) in Line Notify](https://assets.example.com/1*GA_ORi8TX3N8jPSxX4OqHw.png) + +- Click on "Generate token" + +![Generate token](https://assets.example.com/1*qu1mFEhu8f6_bXRvW0uFpw.png) + +- Token Name: Enter the desired bot title, which will be displayed before the message (e.g., `Github Repo Notifer: XXXX`). + +- Choose where the message should be sent: I choose "1-on-1 chat with LINE Notify" to send messages to myself via LINE Notify's official bot. + +- Click on "Generate token" + +![Generate token](https://assets.example.com/1*DMXhPQBiBQH_dTYA4mayHw.png) + +- Select "Copy" + +- **Make sure to note down the Token, as it won't be visible if you forget it and you'll need to generate a new one.** + +`githubWebhookSecret`: + +- Go to [Random.org](https://www.random.org/strings/?num=1&len=32&digits=on&upperalpha=on&loweralpha=on&unique=on&format=html&rnd=new){:target="_blank"} to generate a random string. + +![Generate a random string](https://assets.example.com/1*PD_SqJAmLSHdMYWvdz43_g.png) + +- Copy & remember this random string. + +We will use this string as the verification medium between Github Webhook and Google Apps Script. + +> Due to [GAS limitations](https://issuetracker.google.com/issues/67764685?pli=1){:target="_blank"}, it's not possible to access the `Headers` content in `doPost(e)`. Therefore, the standard authentication method for Github Webhooks [cannot be used](https://docs.github.com/en/webhooks-and-events/webhooks/securing-your-webhooks){:target="_blank"} in Google Apps Script. We can only manually perform string matching authentication using `?secret=`. + +#### Creating Google Apps Script + +Go to **[Google Apps Script](https://script.google.com/home/start){:target="_blank"}**, and click on the top left corner, "+ New Project". + +![**Google Apps Script**](https://assets.example.com/1*ajoOp3ZLc88ecEtYbUVP4A.png) + +[**Google Apps Script**](https://script.google.com/home/start){:target="_blank"} + +Click on the top-left corner, "Untitled Project," to rename the project. + +![Rename the project](https://assets.example.com/1*Gv2KLHa-7qNnL71_jBblGA.png) + +Here, I'll name the project "My-Github-Repo-Notifier" for easy identification in the future. + +**Code Input Area:** + +```javascript + +// Constant variables +const lineToken = 'XXXX'; +// Generate yours line notify bot token: https://notify-bot.line.me/my/ +const githubWebhookSecret = "XXXXX"; +// Generate yours secret string here: https://www.random.org/strings/?num=1&len=32&digits=on&upperalpha=on&loweralpha=on&unique=on&format=html&rnd=new + +// HTTP Get/Post Handler +// Do not allow Get method +function doGet(e) { + return HtmlService.createHtmlOutput("Access Denied!"); + +} + +// Github Webhook will use Post method to enter +function doPost(e) { + + const content = JSON.parse(e.postData.contents); + + // Security check to ensure the request is from Github Webhook + if (verifyGitHubWebhook(e) == false) { + return HtmlService.createHtmlOutput("Access Denied!"); + + } + + // Star payload data content["action"] == "started" + if (content["action"] != "started") { + return HtmlService.createHtmlOutput("OK!"); + + } + + // Compose the message + const message = makeMessageString(content); + + // Send the message, can also be sent to Slack, Telegram... + sendLineNotifyMessage(message); + + return HtmlService.createHtmlOutput("OK!"); + +} + +// Method +// Generate message content +function makeMessageString(content) { + + const repository = content["repository"]; + const repositoryName = repository["name"]; + const repositoryURL = repository["svn_url"]; + const starsCount = repository["stargazers_count"]; + const forksCount = repository["forks_count"]; + const starrer = content["sender"]["login"]; + + var message = "🎉🎉 \"" + starrer + "\" starred your \"" + repositoryName + "\" Repo 🎉🎉\n"; + message += "Current total stars: " + starsCount + "\n"; + message += "Current total forks: " + forksCount + "\n"; + message += repositoryURL; + + return message; +} + +// Verify if the request is from Github Webhook +// Due to GAS limitations (https://issuetracker.google.com/issues/67764685?pli=1) +// It's not possible to get Headers content in doPost(e) +// So the standard authentication method for Github Webhooks (https://docs.github.com/en/webhooks-and-events/webhooks/securing-your-webhooks) +// cannot be used, only manual string matching authentication with ?secret=XXX +function verifyGitHubWebhook(e) { + if (e.parameter["secret"] === githubWebhookSecret) { + return true + } else { + return false + } +} + +// -- Send Message -- +// Line +// For other message delivery methods, you can ask ChatGPT +function sendLineNotifyMessage(message) { + var url = 'https://notify-api.line.me/api/notify'; + var options = { + method: 'post', + headers: { + 'Authorization': 'Bearer ' + lineToken + }, + payload: { + 'message': message + } + + }; + UrlFetchApp.fetch(url, options); +} + +``` + +Replace `lineToken` & `githubWebhookSecret` with the values you copied in the previous steps. + +**Supplemental data received when someone presses Star on Github Webook is as follows:** +```json +{ + "action": "created", + "starred_at": "2023-08-01T03:42:26Z", + "repository": { + "id": 602927147, + "node_id": "R_kgDOI-_wKw", + "name": "ZMarkupParser", + "full_name": "ZhgChgLi/ZMarkupParser", + "private": false, + "owner": { + "login": "ZhgChgLi", + "id": 83232222, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjgzMjMyMjIy", + "avatar_url": "https://avatars.githubusercontent.com/u/83232222?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/ZhgChgLi", + "html_url": "https://github.com/ZhgChgLi", + "followers_url": "https://api.github.com/users/ZhgChgLi/followers", + "following_url": "https://api.github.com/users/ZhgChgLi/following{/other_user}", + "gists_url": "https://api.github.com/users/ZhgChgLi/gists{/gist_id}", + "starred_url": "https://api.github.com/users/ZhgChgLi/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/ZhgChgLi/subscriptions", + "organizations_url": "https://api.github.com/users/ZhgChgLi/orgs", + "repos_url": "https://api.github.com/users/ZhgChgLi/repos", + "events_url": "https://api.github.com/users/ZhgChgLi/events{/privacy}", + "received_events_url": "https://api.github.com/users/ZhgChgLi/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/ZhgChgLi/ZMarkupParser", + "description": "ZMarkupParser is a pure-Swift library that helps you convert HTML strings into NSAttributedString with customized styles and tags.", + "fork": false, + "url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser", + "forks_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/forks", + "keys_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/teams", + "hooks_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/hooks", + "issue_events_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/issues/events{/number}", + "events_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/events", + "assignees_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/assignees{/user}", + "branches_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/branches{/branch}", + "tags_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/tags", + "blobs_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/statuses/{sha}", + "languages_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/languages", + "stargazers_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/stargazers", + "contributors_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/contributors", + "subscribers_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/subscribers", + "subscription_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/subscription", + "commits_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/contents/{+path}", + "compare_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/merges", + "archive_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/downloads", + "issues_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/issues{/number}", + "pulls_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/pulls{/number}", + "milestones_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/milestones{/number}", + "notifications_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/labels{/name}", + "releases_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/releases{/id}", + "deployments_url": "https://api.github.com/repos/ZhgChgLi/ZMarkupParser/deployments", + "created_at": "2023-02-17T08:41:37Z", + "updated_at": "2023-08-01T03:42:27Z", + "pushed_at": "2023-08-01T00:07:41Z", + "git_url": "git://github.com/ZhgChgLi/ZMarkupParser.git", + "ssh_url": "git@github.com:ZhgChgLi/ZMarkupParser.git", + "clone_url": "https://github.com/ZhgChgLi/ZMarkupParser.git", + "svn_url": "https://github.com/ZhgChgLi/ZMarkupParser", + "homepage": "https://zhgchg.li", + "size": 27449, + "stargazers_count": 187, + "watchers_count": 187, + "language": "Swift", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "has_discussions": false, + "forks_count": 10, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 2, + "license": { + "key": "mit", + "name": "MIT License", + "spdx_id": "MIT", + "url": "https://api.github.com/licenses/mit", + "node_id": "MDc6TGljZW5zZTEz" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + "cocoapods", + "html", + "html-converter", + "html-parser", + "html-renderer", + "ios", + "nsattributedstring", + "swift", + "swift-package", + "textfield", + "uikit", + "uilabel", + "uitextview" + ], + "visibility": "public", + "forks": 10, + "open_issues": 2, + "watchers": 187, + "default_branch": "main" + }, + "organization": { + "login": "ZhgChgLi", + "id": 83232222, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjgzMjMyMjIy", + "url": "https://api.github.com/orgs/ZhgChgLi", + "repos_url": "https://api.github.com/orgs/ZhgChgLi/repos", + "events_url": "https://api.github.com/orgs/ZhgChgLi/events", + "hooks_url": "https://api.github.com/orgs/ZhgChgLi/hooks", + "issues_url": "https://api.github.com/orgs/ZhgChgLi/issues", + "members_url": "https://api.github.com/orgs/ZhgChgLi/members{/member}", + "public_members_url": "https://api.github.com/orgs/ZhgChgLi/public_members{/member}", + "avatar_url": "https://avatars.githubusercontent.com/u/83232222?v=4", + "description": "Building a Better World Together." + }, + "sender": { + "login": "zhgtest", + "id": 4601621, + "node_id": "MDQ6VXNlcjQ2MDE2MjE=", + "avatar_url": "https://avatars.githubusercontent.com/u/4601621?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/zhgtest", + "html_url": "https://github.com/zhgtest", + "followers_url": "https://api.github.com/users/zhgtest/followers", + "following_url": "https://api.github.com/users/zhgtest/following{/other_user}", + "gists_url": "https://api.github.com/users/zhgtest/gists{/gist_id}", + "starred_url": "https://api.github.com/users/zhgtest/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/zhgtest/subscriptions", + "organizations_url": "https://api.github.com/users/zhgtest/orgs", + "repos_url": "https://api.github.com/users/zhgtest/repos", + "events_url": "https://api.github.com/users/zhgtest/events{/privacy}", + "received_events_url": "https://api.github.com/users/zhgtest/received_events", + "type": "User", + "site_admin": false + } +} +``` +#### Deployment + +After completing the program writing, click on the upper right corner "Deploy" -> "New Deployment": + +![Deployment](/assets/382218e15697/1*ZBj7EvEXfn0nuRgfotI6IA.png) + +Select the "Web Application" type on the left: + +![Web Application Type](/assets/382218e15697/1*YeOPSIKo6x6f-Qa3ymeT0Q.png) + +![Web Application Type](/assets/382218e15697/1*Gfr2J6OnWpe8664N-3tzMg.png) + +- Add description: Input any text, I entered "`Release`." +- Who can access: **Change it to "`Everyone`."** +- Click "Deploy." + +For the first deployment, click on "Grant access": + +![Grant Access](/assets/382218e15697/1*p7QiiYISmberMJPGQINr1w.png) + +Select your current Gmail account from the popped-up account selection: + +![Select Gmail Account](/assets/382218e15697/1*i3QQ-SLJt7VBtzNsgO7Lqw.png) + +"Google hasn't verified this app" will appear because the App we are developing is for personal use and does not require Google's verification. + +Simply click "Advanced" -> "Go to XXX (unsafe)" -> "Allow": + +![Advanced](/assets/382218e15697/1*kYmtS0WBI-NRXxDuUTrU8A.png) + +![Advanced](/assets/382218e15697/1*G6AAaLVU9LUS-FCqxv_JWw.png) + +![Advanced](/assets/382218e15697/1*jm4_8EKQnPHctrtmnO2t2g.png) + +After completing the deployment, you can find the Request URL in the "Web Application" section on the result page. Click "Copy" and take note of this GAS URL. + +![Request URL](/assets/382218e15697/1*e_nw9Zvcl1dTSulg1KVhOw.png) + +**⚠️️️ By the way, please note that if there are code modifications, you need to update the deployment for the changes to take effect.⚠️** + +To make the changes in the code effective, click on "Deploy" -> select "Manage Deployments" -> choose the "✏️" in the upper right corner -> select "Create New Version" -> click "Deploy." + +![Manage Deployments](/assets/382218e15697/1*vYYM-Gy3Gyou15UhJWmSOA.png) + +![Manage Deployments](/assets/382218e15697/1*RBTSeK1kQ3JSZJBgPnAAWA.png) + +This will complete the code update deployment. + +#### Github Webhook Configuration + +- Go back to [Github](https://github.com/){:target="_blank"} +- We can set up a webhook for Organizations (all Repos inside) or a single Repo to monitor new ⭐️ Stars. + +Go to Organizations / Repo -> "Settings" -> find "Webhooks" on the left -> "Add webhook": + +![Webhook Settings](/assets/382218e15697/1*Ke5ZarGC8ODrFLj8LsBNFg.png) + +![Webhook Settings](/assets/382218e15697/1*PUHcpcJkbL4d7xTI5A99PA.png) + +- Payload URL **: ** Enter the `GAS URL` and manually add our own security verification string at the end of the URL `?secret=githubWebhookSecret`. +For example, if your `GAS URL` is `https://script.google.com/macros/s/XXX/exec` and `githubWebhookSecret` is `123456`, then the URL will be: `https://script.google.com/macros/s/XXX/exec?secret=123456`. +- **Content type: ** Choose `application/json`. +- **Which events would you like to trigger this webhook?** +Choose "**Let me select individual events.**" +**⚠️️Uncheck "Pushes".** +**️️️️⚠️Check "Watches". Please note that it's not "Stars" (though Stars also monitor the state of stars clicked, if you use Stars for GAS actions, you will need to adjust accordingly.)** +- Choose "Active". +- Click "Add webhook". +- Configuration completed. + +#### 🚀 Testing + +Go back to the Organizations Repo / Repo where the settings were made, click "Star" or un-star and then re-star: + +![Testing](/assets/382218e15697/1*0CaNDxOub_Eo_byb-WfpTQ.png) + +You will receive a push notification! + +![Notification](/assets/382218e15697/1*hKN9lAQTsm-tOnJSLj3GGw.png) + +Done! 🎉🎉🎉🎉 + +### Ad Timing! + + +[![](https://repository-images.githubusercontent.com/602927147/57ce75c1-8548-449c-b44a-f4b0451ed5ea)](https://github.com/ZhgChgLi/ZMarkupParser){:target="_blank"} + + + +[![](https://repository-images.githubusercontent.com/516425682/1cc1a829-d87d-4d4a-925b-60471b912b23)](https://github.com/ZhgChgLi/ZReviewTender){:target="_blank"} + + + +[![Like Z Realm's work](https://button.like.co/images/og/likebutton.png "Like Z Realm's work")](https://button.like.co/in/like/zhgchgli){:target="_blank"} + + +Any questions or suggestions are welcome. Please feel free to [contact me](https://www.zhgchg.li/contact){:target="_blank"}. + + + +_[Post](https://medium.com/zrealm-ios-dev/%E4%BD%BF%E7%94%A8-google-apps-script-%E4%B8%89%E6%AD%A5%E9%A9%9F%E5%85%8D%E8%B2%BB%E5%BB%BA%E7%AB%8B-github-repo-star-notifier-382218e15697){:target="_blank"} converted from Medium by [ZMediumToMarkdown](https://github.com/ZhgChgLi/ZMediumToMarkdown){:target="_blank"}._ diff --git a/_posts/zmediumtomarkdown/2023-08-01-382218e15697.md b/_posts/zmediumtomarkdown/2023-08-01-382218e15697.md index 9f16388fd..6746bd7b4 100644 --- a/_posts/zmediumtomarkdown/2023-08-01-382218e15697.md +++ b/_posts/zmediumtomarkdown/2023-08-01-382218e15697.md @@ -1,8 +1,8 @@ --- title: "使用 Google Apps Script 三步驟免費建立 Github Repo Star Notifier" author: "ZhgChgLi" -date: 2023-08-01T14:32:14.687+0000 -last_modified_at: 2023-08-01T14:32:14.687+0000 +date: 2023-08-01T21:32:14.687+0000 +last_modified_at: 2023-08-01T21:32:14.687+0000 categories: "ZRealm Dev." tags: ["ios-app-development","google-app-script","github","notifications","stars"] description: "撰寫 GAS 串接 Github Webhook 轉發按星星 Like 通知到 Line"