From 5dd681c5a91079fa370010dfdc9a23e495f58580 Mon Sep 17 00:00:00 2001 From: Jonah Lawrence Date: Fri, 23 Dec 2022 12:30:34 -0700 Subject: [PATCH 01/25] feat: Added transparent to valid CSS colors list (#388) --- src/colors.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/colors.php b/src/colors.php index 1f166f2d..9003f8a9 100644 --- a/src/colors.php +++ b/src/colors.php @@ -27,8 +27,8 @@ "darkcyan", "darkgoldenrod", "darkgray", - "darkgrey", "darkgreen", + "darkgrey", "darkkhaki", "darkmagenta", "darkolivegreen", @@ -56,9 +56,9 @@ "gold", "goldenrod", "gray", - "grey", "green", "greenyellow", + "grey", "honeydew", "hotpink", "indianred", @@ -74,8 +74,8 @@ "lightcyan", "lightgoldenrodyellow", "lightgray", - "lightgrey", "lightgreen", + "lightgrey", "lightpink", "lightsalmon", "lightseagreen", @@ -143,6 +143,7 @@ "teal", "thistle", "tomato", + "transparent", "turquoise", "violet", "wheat", From f0abb6df0d7961fdb837e7f05ed73257147f17e6 Mon Sep 17 00:00:00 2001 From: Jonah Lawrence Date: Sun, 25 Dec 2022 12:28:04 -0700 Subject: [PATCH 02/25] docs(readme): Added steps for deploying to Vercel (#391) --- README.md | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 07436529..88f79ec3 100644 --- a/README.md +++ b/README.md @@ -144,8 +144,11 @@ The Inkscape dependency is required for PNG rendering, as well as Segoe UI font [![Heroku_logo](https://user-images.githubusercontent.com/20955511/136292872-ab2b3918-3350-4878-93a2-aa1f569b095a.png)](https://heroku.com) +Heroku costs around $5-$7/month minimum for a single app, but you can contact the Open Source program +at ospo-heroku-credits@salesforce.com to possibly get free credits. +
- Instructions for Deploying to Heroku for Free + Instructions for Deploying to Heroku ### Step-by-step instructions for deploying to Heroku @@ -165,6 +168,31 @@ The Inkscape dependency is required for PNG rendering, as well as Segoe UI font
+[![Vercel_logo](https://user-images.githubusercontent.com/20955511/209479243-5b14048b-e9ae-42da-aec3-1cc88a97aaee.png)](https://vercel.com) + +Vercel is a free hosting service that can be used to run PHP. **Note:** The intl library +seems to not be available through Vercel at the moment, so the automatic number and date +formats for locales other than English will not work. + +
+ Instructions for Deploying to Vercel for Free + +### Step-by-step instructions for deploying to Vercel + +1. Sign in to **Vercel** or create a new account at +2. Clone this repository with `git clone https://github.com/DenverCoder1/github-readme-streak-stats.git` + - You may also fork the repository and clone your fork instead if you intend to make changes +3. Enter the directory with `cd github-readme-streak-stats` +4. Switch branches to the `vercel` branch with `git checkout vercel` +5. Make sure you have the [Vercel CLI](https://vercel.com/download) installed +6. Run `vercel` and follow the prompts to link your Vercel account and select a project name +7. The app will be deployed to `.vercel.app` +8. Visit [this link](https://github.com/settings/tokens/new?description=GitHub%20Readme%20Streak%20Stats) to create a new Personal Access Token (no scopes required) +9. Scroll to the bottom and click **"Generate token"** +10. Visit the [Vercel dashboard](https://vercel.com/dashboard) and select your project, then click **"Settings"**, then **"Environment Variables"**. Add a new variable with the key `TOKEN` and the value as your token from step 9. + +
+ ## 🤗 Contributing Contributions are welcome! Feel free to [open an issue](https://github.com/DenverCoder1/github-readme-streak-stats/issues/new/choose) or submit a [pull request](https://github.com/DenverCoder1/github-readme-streak-stats/compare) if you have a way to improve this project. From e2516c97400cdcf157fcd80f140a2bfee128b739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B3=D0=BE=D1=80=20=D0=9C=D0=B0=D1=80=D1=82=D1=8B?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2?= <48406064+mrtnvgr@users.noreply.github.com> Date: Tue, 27 Dec 2022 02:08:51 +0700 Subject: [PATCH 03/25] feat: Update Russian translations (#392) --- src/translations.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/translations.php b/src/translations.php index ce1a14f7..903b3e21 100644 --- a/src/translations.php +++ b/src/translations.php @@ -177,6 +177,8 @@ "Total Contributions" => "Общий вклад", "Current Streak" => "Текущая серия", "Longest Streak" => "Самая длинная серия", + "Week Streak" => "Текущая серия недель", + "Longest Week Streak" => "Самая длинная серия недель", "Present" => "Сейчас", ], "ta" => [ From 19e4ca8a2e1f7bffc2e4f2b4130e33a27a916e57 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 26 Dec 2022 19:09:13 +0000 Subject: [PATCH 04/25] docs(readme): Update translation progress --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88f79ec3..4387b334 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ To enable a theme, append `&theme=` followed by the theme name to the end of the -
en - English
English 100%
ar - العربية
العربية 100%
bn - বাংলা
বাংলা 100%
es - español
español 100%
fa - فارسی
فارسی 100%
he - עברית
עברית 100%
hi - हिन्दी
हिन्दी 100%
ja - 日本語
日本語 100%
ko - 한국어
한국어 100%
mr - मराठी
मराठी 100%
pl - polski
polski 100%
pt_BR - português (Brasil)
português (Brasil) 100%
zh_Hans - 中文(简体)
中文(简体) 100%
zh_Hant - 中文(繁體)
中文(繁體) 100%
da - dansk
dansk 67%
de - Deutsch
Deutsch 67%
fr - français
français 67%
id - Indonesia
Indonesia 67%
it - italiano
italiano 67%
kn - ಕನ್ನಡ
ಕನ್ನಡ 67%
nl - Nederlands
Nederlands 67%
ru - русский
русский 67%
ta - தமிழ்
தமிழ் 67%
tr - Türkçe
Türkçe 67%
vi - Tiếng Việt
Tiếng Việt 67%
+
en - English
English 100%
ar - العربية
العربية 100%
bn - বাংলা
বাংলা 100%
es - español
español 100%
fa - فارسی
فارسی 100%
he - עברית
עברית 100%
hi - हिन्दी
हिन्दी 100%
ja - 日本語
日本語 100%
ko - 한국어
한국어 100%
mr - मराठी
मराठी 100%
pl - polski
polski 100%
pt_BR - português (Brasil)
português (Brasil) 100%
ru - русский
русский 100%
zh_Hans - 中文(简体)
中文(简体) 100%
zh_Hant - 中文(繁體)
中文(繁體) 100%
da - dansk
dansk 67%
de - Deutsch
Deutsch 67%
fr - français
français 67%
id - Indonesia
Indonesia 67%
it - italiano
italiano 67%
kn - ಕನ್ನಡ
ಕನ್ನಡ 67%
nl - Nederlands
Nederlands 67%
ta - தமிழ்
தமிழ் 67%
tr - Türkçe
Türkçe 67%
vi - Tiếng Việt
Tiếng Việt 67%
From 898015e12e0469f5872233e6903552cbf03c115e Mon Sep 17 00:00:00 2001 From: Jonah Lawrence Date: Mon, 26 Dec 2022 16:52:57 -0700 Subject: [PATCH 05/25] docs(readme): Update vercel instructions with Git note and env image (#393) --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4387b334..2c6c747a 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,13 @@ formats for locales other than English will not work. 7. The app will be deployed to `.vercel.app` 8. Visit [this link](https://github.com/settings/tokens/new?description=GitHub%20Readme%20Streak%20Stats) to create a new Personal Access Token (no scopes required) 9. Scroll to the bottom and click **"Generate token"** -10. Visit the [Vercel dashboard](https://vercel.com/dashboard) and select your project, then click **"Settings"**, then **"Environment Variables"**. Add a new variable with the key `TOKEN` and the value as your token from step 9. +10. Visit the [Vercel dashboard](https://vercel.com/dashboard) and select your project, then click **"Settings"**, then **"Environment Variables"**. +11. Add a new variable with the key `TOKEN` and the value as your token from step 9 and click "Save". + +![image](https://user-images.githubusercontent.com/20955511/209588756-8bf5b0cd-9aa6-41e8-909c-97bf41e525b3.png) + +> **Note** +> To set up automatic Vercel deployments from GitHub, make sure to turn **off** "Include source files outside of the Root Directory" in the General settings and use `vercel` as the production branch in the Git settings. From 0332281b27a5bdd948d7f4bf4e34a7e8f906fef6 Mon Sep 17 00:00:00 2001 From: Jonah Lawrence Date: Mon, 26 Dec 2022 21:03:31 -0700 Subject: [PATCH 06/25] refactor: Convert to PNG without using files (#394) --- src/card.php | 86 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 26 deletions(-) diff --git a/src/card.php b/src/card.php index 08025205..98a41842 100644 --- a/src/card.php +++ b/src/card.php @@ -500,50 +500,84 @@ function convertSvgToPng(string $svg): string $svg = preg_replace("/(animation: currstreak[^;'\"]+)/m", "font-size: 28px;", $svg); $svg = preg_replace("/(\X*?)<\/a>/m", '\1', $svg); - // save svg to random file - $filename = uniqid(); - file_put_contents("$filename.svg", $svg); + // escape svg for shell + $svg = escapeshellarg($svg); + + // `--pipe`: read input from pipe (stdin) + // `--export-filename -`: write output to stdout + // `-w 495 -h 195`: set width and height of the output image + // `--export-type png`: set the output format to PNG + $cmd = "echo {$svg} | inkscape --pipe --export-filename - -w 495 -h 195 --export-type png"; // convert svg to png - $out = shell_exec("inkscape -w 495 -h 195 {$filename}.svg -o {$filename}.png"); // skipcq: PHP-A1009 - if ($out !== null) { - throw new Exception("Error converting SVG to PNG: $out"); + $png = shell_exec($cmd); // skipcq: PHP-A1009 + + // check if the conversion was successful + if (empty($png)) { + // `2>&1`: redirect stderr to stdout + $error = shell_exec("$cmd 2>&1"); // skipcq: PHP-A1009 + throw new Exception("Failed to convert SVG to PNG: {$error}", 500); } - // read png data and delete temporary files - $png = file_get_contents("{$filename}.png"); - unlink("{$filename}.svg"); - unlink("{$filename}.png"); + // return the generated png return $png; } /** - * Set headers and echo response based on type + * Return headers and response based on type * * @param string|array $output The stats (array) or error message (string) to display + * + * @return array The Content-Type header and the response body, and status code in case of an error */ -function renderOutput(string|array $output, int $responseCode = 200): void +function generateOutput(string|array $output): array { $requestedType = $_REQUEST["type"] ?? "svg"; - http_response_code($responseCode); // output JSON data if ($requestedType === "json") { - // set content type to JSON - header("Content-Type: application/json"); // generate array from output $data = gettype($output) === "string" ? ["error" => $output] : $output; - // output as JSON - echo json_encode($data); + return [ + "contentType" => "application/json", + "body" => json_encode($data), + ]; } - // output SVG or PNG card - else { - // set content type to SVG or PNG - header("Content-Type: image/" . ($requestedType === "png" ? "png" : "svg+xml")); - // render SVG card - $svg = gettype($output) === "string" ? generateErrorCard($output) : generateCard($output); - // output PNG if PNG is requested, otherwise output SVG - echo $requestedType === "png" ? convertSvgToPng($svg) : $svg; + // Generate SVG card + $svg = gettype($output) === "string" ? generateErrorCard($output) : generateCard($output); + // output PNG card + if ($requestedType === "png") { + try { + $png = convertSvgToPng($svg); + return [ + "contentType" => "image/png", + "body" => $png, + ]; + } catch (Exception $e) { + return [ + "contentType" => "image/svg+xml", + "status" => 500, + "body" => generateErrorCard($e->getMessage()), + ]; + } } - exit(); + // output SVG card + return [ + "contentType" => "image/svg+xml", + "body" => $svg, + ]; +} + +/** + * Set headers and output response + * + * @param string|array $output The Content-Type header and the response body + * @param int $responseCode The HTTP response code to send + */ +function renderOutput(string|array $output, int $responseCode = 200): void +{ + $response = generateOutput($output); + http_response_code($response["status"] ?? $responseCode); + header("Content-Type: {$response["contentType"]}"); + exit($response["body"]); } From 28f333f87911a675cde3ee2671cfdb8ddaf3ba00 Mon Sep 17 00:00:00 2001 From: Jonah Lawrence Date: Mon, 26 Dec 2022 21:33:31 -0700 Subject: [PATCH 07/25] docs(readme): Update Vercel information (#395) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2c6c747a..eb865c9d 100644 --- a/README.md +++ b/README.md @@ -170,9 +170,9 @@ at ospo-heroku-credits@salesforce.com to possibly get free credits. [![Vercel_logo](https://user-images.githubusercontent.com/20955511/209479243-5b14048b-e9ae-42da-aec3-1cc88a97aaee.png)](https://vercel.com) -Vercel is a free hosting service that can be used to run PHP. **Note:** The intl library -seems to not be available through Vercel at the moment, so the automatic number and date -formats for locales other than English will not work. +Vercel is a free hosting service that can be used to run PHP. **Note:** The intl library seems to not be available through Vercel at the moment +(https://github.com/vercel-community/php/issues/367), so the automatic number and date formats for locales other than English will not work. +PNG mode is also not supported since Inkscape will not be installed.
Instructions for Deploying to Vercel for Free From 4e8f36c0b7855d7d692d4633b02df1c0d6e8d2fa Mon Sep 17 00:00:00 2001 From: Halim Shams <96909515+Halim-Shams@users.noreply.github.com> Date: Thu, 29 Dec 2022 00:56:51 +0430 Subject: [PATCH 08/25] feat: Added Pashto translations (#396) --- src/translations.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/translations.php b/src/translations.php index 903b3e21..e4e650ff 100644 --- a/src/translations.php +++ b/src/translations.php @@ -165,6 +165,14 @@ "Longest Week Streak" => "Najdłuższa Seria Tygodni", "Present" => "Dziś", ], + "ps" => [ + "Total Contributions" => "ټولې ونډې", + "Current Streak" => "اوسنی پرمختګ", + "Longest Streak" => "تر ټولو اوږد پرمختګ", + "Week Streak" => "د اونۍ پرمختګ", + "Longest Week Streak" => "د اونۍ تر ټولو اوږد پرمختګ", + "Present" => "اوس", + ], "pt_BR" => [ "Total Contributions" => "Total de Contribuições", "Current Streak" => "Sequência Atual", From d6ac8a1ad75acbf32c4d7c138b0f36a6bd18e35f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 28 Dec 2022 20:27:15 +0000 Subject: [PATCH 09/25] docs(readme): Update translation progress --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb865c9d..e7130282 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ To enable a theme, append `&theme=` followed by the theme name to the end of the -
en - English
English 100%
ar - العربية
العربية 100%
bn - বাংলা
বাংলা 100%
es - español
español 100%
fa - فارسی
فارسی 100%
he - עברית
עברית 100%
hi - हिन्दी
हिन्दी 100%
ja - 日本語
日本語 100%
ko - 한국어
한국어 100%
mr - मराठी
मराठी 100%
pl - polski
polski 100%
pt_BR - português (Brasil)
português (Brasil) 100%
ru - русский
русский 100%
zh_Hans - 中文(简体)
中文(简体) 100%
zh_Hant - 中文(繁體)
中文(繁體) 100%
da - dansk
dansk 67%
de - Deutsch
Deutsch 67%
fr - français
français 67%
id - Indonesia
Indonesia 67%
it - italiano
italiano 67%
kn - ಕನ್ನಡ
ಕನ್ನಡ 67%
nl - Nederlands
Nederlands 67%
ta - தமிழ்
தமிழ் 67%
tr - Türkçe
Türkçe 67%
vi - Tiếng Việt
Tiếng Việt 67%
+
en - English
English 100%
ar - العربية
العربية 100%
bn - বাংলা
বাংলা 100%
es - español
español 100%
fa - فارسی
فارسی 100%
he - עברית
עברית 100%
hi - हिन्दी
हिन्दी 100%
ja - 日本語
日本語 100%
ko - 한국어
한국어 100%
mr - मराठी
मराठी 100%
pl - polski
polski 100%
ps - پښتو
پښتو 100%
pt_BR - português (Brasil)
português (Brasil) 100%
ru - русский
русский 100%
zh_Hans - 中文(简体)
中文(简体) 100%
zh_Hant - 中文(繁體)
中文(繁體) 100%
da - dansk
dansk 67%
de - Deutsch
Deutsch 67%
fr - français
français 67%
id - Indonesia
Indonesia 67%
it - italiano
italiano 67%
kn - ಕನ್ನಡ
ಕನ್ನಡ 67%
nl - Nederlands
Nederlands 67%
ta - தமிழ்
தமிழ் 67%
tr - Türkçe
Türkçe 67%
vi - Tiếng Việt
Tiếng Việt 67%
From e64eecd64c0a697ba8607d921ce9dbcbcb824d7f Mon Sep 17 00:00:00 2001 From: Mohammad Ismail <96207520+mouismail@users.noreply.github.com> Date: Thu, 29 Dec 2022 19:39:09 +0100 Subject: [PATCH 10/25] fix: Fixed Arabic translations (#397) --- src/translations.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/translations.php b/src/translations.php index e4e650ff..bc93a3ee 100644 --- a/src/translations.php +++ b/src/translations.php @@ -42,10 +42,10 @@ "ar" => [ "rtl" => true, "Total Contributions" => "إجمالي المساهمات", - "Current Streak" => "حالِيا سلسلة متتالية", - "Longest Streak" => "طَويل سلسلة متتالية", - "Week Streak" => "أُسْبوع سلسلة متتالية", - "Longest Week Streak" => "طَويل أُسْبوع سلسلة متتالية", + "Current Streak" => "السلسلة المتتالية الحالية", + "Longest Streak" => "أُطول سلسلة متتالية", + "Week Streak" => "السلسلة المتتالية الأُسبوعية", + "Longest Week Streak" => "أُطول سلسلة متتالية أُسبوعية", "Present" => "الحاضر", ], "bn" => [ From c3853ec610f9edd4157550ca23b999519750445e Mon Sep 17 00:00:00 2001 From: Ivan Gechev <44908454+Banovvv@users.noreply.github.com> Date: Fri, 30 Dec 2022 17:48:43 +0200 Subject: [PATCH 11/25] feat: Added Bulgarian translations (#398) --- src/translations.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/translations.php b/src/translations.php index bc93a3ee..c5257ef1 100644 --- a/src/translations.php +++ b/src/translations.php @@ -48,6 +48,14 @@ "Longest Week Streak" => "أُطول سلسلة متتالية أُسبوعية", "Present" => "الحاضر", ], + "bg" => [ + "Total Contributions" => "Общ принос", + "Current Streak" => "Дневна серия", + "Longest Streak" => "Най-дълга дневна серия", + "Week Streak" => "Седмична серия", + "Longest Week Streak" => "Най-дълга седмична серия", + "Present" => "Сега", + ], "bn" => [ "Total Contributions" => "মোট অবদান", "Current Streak" => "কারেন্ট স্ট্রীক", From 42fbd93829d5d4d66d3a0c956eeda42c4d9560d4 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 30 Dec 2022 15:49:11 +0000 Subject: [PATCH 12/25] docs(readme): Update translation progress --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e7130282..42e80517 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ To enable a theme, append `&theme=` followed by the theme name to the end of the -
en - English
English 100%
ar - العربية
العربية 100%
bn - বাংলা
বাংলা 100%
es - español
español 100%
fa - فارسی
فارسی 100%
he - עברית
עברית 100%
hi - हिन्दी
हिन्दी 100%
ja - 日本語
日本語 100%
ko - 한국어
한국어 100%
mr - मराठी
मराठी 100%
pl - polski
polski 100%
ps - پښتو
پښتو 100%
pt_BR - português (Brasil)
português (Brasil) 100%
ru - русский
русский 100%
zh_Hans - 中文(简体)
中文(简体) 100%
zh_Hant - 中文(繁體)
中文(繁體) 100%
da - dansk
dansk 67%
de - Deutsch
Deutsch 67%
fr - français
français 67%
id - Indonesia
Indonesia 67%
it - italiano
italiano 67%
kn - ಕನ್ನಡ
ಕನ್ನಡ 67%
nl - Nederlands
Nederlands 67%
ta - தமிழ்
தமிழ் 67%
tr - Türkçe
Türkçe 67%
vi - Tiếng Việt
Tiếng Việt 67%
+
en - English
English 100%
ar - العربية
العربية 100%
bg - български
български 100%
bn - বাংলা
বাংলা 100%
es - español
español 100%
fa - فارسی
فارسی 100%
he - עברית
עברית 100%
hi - हिन्दी
हिन्दी 100%
ja - 日本語
日本語 100%
ko - 한국어
한국어 100%
mr - मराठी
मराठी 100%
pl - polski
polski 100%
ps - پښتو
پښتو 100%
pt_BR - português (Brasil)
português (Brasil) 100%
ru - русский
русский 100%
zh_Hans - 中文(简体)
中文(简体) 100%
zh_Hant - 中文(繁體)
中文(繁體) 100%
da - dansk
dansk 67%
de - Deutsch
Deutsch 67%
fr - français
français 67%
id - Indonesia
Indonesia 67%
it - italiano
italiano 67%
kn - ಕನ್ನಡ
ಕನ್ನಡ 67%
nl - Nederlands
Nederlands 67%
ta - தமிழ்
தமிழ் 67%
tr - Türkçe
Türkçe 67%
vi - Tiếng Việt
Tiếng Việt 67%
From 883e427b7092d828017fa59bfd6db7cf0f1b6f13 Mon Sep 17 00:00:00 2001 From: MyNameIsKitsune Date: Sun, 1 Jan 2023 02:54:55 +0200 Subject: [PATCH 13/25] feat: Added Ukrainian Language (#399) Co-authored-by: Jonah Lawrence --- src/translations.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/translations.php b/src/translations.php index c5257ef1..b01eb9ba 100644 --- a/src/translations.php +++ b/src/translations.php @@ -209,6 +209,14 @@ "Longest Streak" => "En Uzun Seri", "Present" => "Şu an", ], + "uk" => [ + "Total Contributions" => "Загальний вклад", + "Current Streak" => "Поточна діяльність", + "Longest Streak" => "Найдовша діяльність", + "Week Streak" => "Діяльність за тиждень", + "Longest Week Streak" => "Найбільша к-сть тижнів", + "Present" => "Наразі", + ], "vi" => [ "Total Contributions" => "Tổng số đóng góp", "Current Streak" => "Chuỗi đóng góp\nhiện tại", From 8472129bd3bbe54de99af5ad82002b25626d81fb Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 1 Jan 2023 00:55:16 +0000 Subject: [PATCH 14/25] docs(readme): Update translation progress --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 42e80517..8285e57e 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ To enable a theme, append `&theme=` followed by the theme name to the end of the -
en - English
English 100%
ar - العربية
العربية 100%
bg - български
български 100%
bn - বাংলা
বাংলা 100%
es - español
español 100%
fa - فارسی
فارسی 100%
he - עברית
עברית 100%
hi - हिन्दी
हिन्दी 100%
ja - 日本語
日本語 100%
ko - 한국어
한국어 100%
mr - मराठी
मराठी 100%
pl - polski
polski 100%
ps - پښتو
پښتو 100%
pt_BR - português (Brasil)
português (Brasil) 100%
ru - русский
русский 100%
zh_Hans - 中文(简体)
中文(简体) 100%
zh_Hant - 中文(繁體)
中文(繁體) 100%
da - dansk
dansk 67%
de - Deutsch
Deutsch 67%
fr - français
français 67%
id - Indonesia
Indonesia 67%
it - italiano
italiano 67%
kn - ಕನ್ನಡ
ಕನ್ನಡ 67%
nl - Nederlands
Nederlands 67%
ta - தமிழ்
தமிழ் 67%
tr - Türkçe
Türkçe 67%
vi - Tiếng Việt
Tiếng Việt 67%
+
en - English
English 100%
ar - العربية
العربية 100%
bg - български
български 100%
bn - বাংলা
বাংলা 100%
es - español
español 100%
fa - فارسی
فارسی 100%
he - עברית
עברית 100%
hi - हिन्दी
हिन्दी 100%
ja - 日本語
日本語 100%
ko - 한국어
한국어 100%
mr - मराठी
मराठी 100%
pl - polski
polski 100%
ps - پښتو
پښتو 100%
pt_BR - português (Brasil)
português (Brasil) 100%
ru - русский
русский 100%
uk - українська
українська 100%
zh_Hans - 中文(简体)
中文(简体) 100%
zh_Hant - 中文(繁體)
中文(繁體) 100%
da - dansk
dansk 67%
de - Deutsch
Deutsch 67%
fr - français
français 67%
id - Indonesia
Indonesia 67%
it - italiano
italiano 67%
kn - ಕನ್ನಡ
ಕನ್ನಡ 67%
nl - Nederlands
Nederlands 67%
ta - தமிழ்
தமிழ் 67%
tr - Türkçe
Türkçe 67%
vi - Tiếng Việt
Tiếng Việt 67%
From 772e4f1dbee42341221b2838f7c696b640b00114 Mon Sep 17 00:00:00 2001 From: Jonah Lawrence Date: Mon, 2 Jan 2023 10:07:22 -0700 Subject: [PATCH 15/25] fix: Only decode json if it's a string (#401) --- src/stats.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stats.php b/src/stats.php index e77aa74c..809f1462 100644 --- a/src/stats.php +++ b/src/stats.php @@ -155,7 +155,7 @@ function fetchGraphQL(string $query): stdClass $ch = getGraphQLCurlHandle($query); $response = curl_exec($ch); curl_close($ch); - $obj = json_decode($response); + $obj = is_string($response) ? json_decode($response) : null; // handle curl errors if ($response === false || $obj === null || curl_getinfo($ch, CURLINFO_HTTP_CODE) >= 400) { // set response code to curl error code From c4409a7f4672e2e3c031ab03eccf7cea5fffc850 Mon Sep 17 00:00:00 2001 From: Jonah Lawrence Date: Mon, 2 Jan 2023 10:16:42 -0700 Subject: [PATCH 16/25] fix: Handle curl errors for single graphQL (#402) --- src/stats.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/stats.php b/src/stats.php index 809f1462..34792bbe 100644 --- a/src/stats.php +++ b/src/stats.php @@ -168,6 +168,10 @@ function fetchGraphQL(string $query): stdClass if ($obj && $obj->message) { throw new AssertionError("Error: $obj->message \n", 401); } + // Handle curl errors + if (curl_errno($ch)) { + throw new AssertionError("cURL error: " . curl_error($ch) . "\n", 500); + } throw new AssertionError("An error occurred when getting a response from GitHub.\n", 502); } return $obj; From 5555b3d622f2c8e7bcadce78af32a12333a2f56c Mon Sep 17 00:00:00 2001 From: Jonah Lawrence Date: Mon, 2 Jan 2023 13:30:16 -0700 Subject: [PATCH 17/25] fix: Don't retry if rate limit exceeded (#403) --- src/stats.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/stats.php b/src/stats.php index 34792bbe..ba54c068 100644 --- a/src/stats.php +++ b/src/stats.php @@ -67,6 +67,12 @@ function getContributionGraphs(string $user): array $decoded = is_string($contents) ? json_decode($contents) : null; // if response is empty or invalid, retry request one time if (empty($decoded) || empty($decoded->data)) { + // if rate limit is exceeded, don't retry + $message = $decoded->errors[0]->message ?? ($decoded->message ?? "An API error occurred."); + if (str_contains($message, "rate limit exceeded")) { + error_log("Error: $message"); + continue; + } $query = buildContributionGraphQuery($user, $year); $request = getGraphQLCurlHandle($query); $contents = curl_exec($request); From 5c5b69462bcc117509f288613302124382d9ce65 Mon Sep 17 00:00:00 2001 From: Jonah Lawrence Date: Tue, 3 Jan 2023 12:00:51 -0700 Subject: [PATCH 18/25] fix: Prevent reusing tokens when rate limited (#406) --- src/stats.php | 79 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/src/stats.php b/src/stats.php index ba54c068..c81fe1f7 100644 --- a/src/stats.php +++ b/src/stats.php @@ -43,12 +43,14 @@ function getContributionGraphs(string $user): array // Get the years the user has contributed $contributionYears = getContributionYears($user); // build a list of individual requests + $tokens = []; $requests = []; foreach ($contributionYears as $year) { // create query for year $query = buildContributionGraphQuery($user, $year); // create curl request - $requests[$year] = getGraphQLCurlHandle($query); + $tokens[$year] = getGitHubToken(); + $requests[$year] = getGraphQLCurlHandle($query, $tokens[$year]); } // build multi-curl handle $multi = curl_multi_init(); @@ -67,19 +69,23 @@ function getContributionGraphs(string $user): array $decoded = is_string($contents) ? json_decode($contents) : null; // if response is empty or invalid, retry request one time if (empty($decoded) || empty($decoded->data)) { - // if rate limit is exceeded, don't retry + // if rate limit is exceeded, don't retry with same token $message = $decoded->errors[0]->message ?? ($decoded->message ?? "An API error occurred."); if (str_contains($message, "rate limit exceeded")) { - error_log("Error: $message"); - continue; + removeGitHubToken($tokens[$year]); } + error_log("First attempt to decode response for $user's $year contributions failed. $message"); $query = buildContributionGraphQuery($user, $year); - $request = getGraphQLCurlHandle($query); + $token = getGitHubToken(); + $request = getGraphQLCurlHandle($query, $token); $contents = curl_exec($request); $decoded = is_string($contents) ? json_decode($contents) : null; // if the response is still empty or invalid, log an error and skip the year if (empty($decoded) || empty($decoded->data)) { $message = $decoded->errors[0]->message ?? ($decoded->message ?? "An API error occurred."); + if (str_contains($message, "rate limit exceeded")) { + removeGitHubToken($token); + } error_log("Failed to decode response for $user's $year contributions after 2 attempts. $message"); continue; } @@ -118,16 +124,46 @@ function getGitHubTokens() return $tokens; } +/** + * Get a token from the token pool + * + * @throws AssertionError if no tokens are available + */ +function getGitHubToken() +{ + $all_tokens = getGitHubTokens(); + return $all_tokens[array_rand($all_tokens)]; +} + +/** + * Remove a token from the token pool + * + * @param string $token Token to remove + */ +function removeGitHubToken(string $token) +{ + $index = array_search($token, $GLOBALS["ALL_TOKENS"]); + if ($index !== false) { + unset($GLOBALS["ALL_TOKENS"][$index]); + } + // if there is no available token, throw an error + if (empty($GLOBALS["ALL_TOKENS"])) { + throw new AssertionError( + "We are being rate-limited! Check
git.io/streak-ratelimit for details.", + 429 + ); + } +} + /** Create a CurlHandle for a POST request to GitHub's GraphQL API * * @param string $query GraphQL query + * @param string $token GitHub token to use for the request * * @return CurlHandle The curl handle for the request */ -function getGraphQLCurlHandle(string $query) +function getGraphQLCurlHandle(string $query, string $token) { - $all_tokens = getGitHubTokens(); - $token = $all_tokens[array_rand($all_tokens)]; $headers = [ "Authorization: bearer $token", "Content-Type: application/json", @@ -151,19 +187,24 @@ function getGraphQLCurlHandle(string $query) * Create a POST request to GitHub's GraphQL API * * @param string $query GraphQL query + * @param string $token GitHub token to use for the request * * @return stdClass An object from the json response of the request * * @throws AssertionError If SSL verification fails */ -function fetchGraphQL(string $query): stdClass +function fetchGraphQL(string $query, string $token): stdClass { - $ch = getGraphQLCurlHandle($query); + $ch = getGraphQLCurlHandle($query, $token); $response = curl_exec($ch); curl_close($ch); - $obj = is_string($response) ? json_decode($response) : null; + $decoded = is_string($response) ? json_decode($response) : null; // handle curl errors - if ($response === false || $obj === null || curl_getinfo($ch, CURLINFO_HTTP_CODE) >= 400) { + if ($response === false || $decoded === null || curl_getinfo($ch, CURLINFO_HTTP_CODE) >= 400) { + $message = $decoded->errors[0]->message ?? ($decoded->message ?? ""); + if (str_contains($message, "rate limit exceeded")) { + removeGitHubToken($token); + } // set response code to curl error code http_response_code(curl_getinfo($ch, CURLINFO_HTTP_CODE)); // Missing SSL certificate @@ -171,8 +212,8 @@ function fetchGraphQL(string $query): stdClass throw new AssertionError("You don't have a valid SSL Certificate installed or XAMPP.", 400); } // Handle errors such as "Bad credentials" - if ($obj && $obj->message) { - throw new AssertionError("Error: $obj->message \n", 401); + if ($message) { + throw new AssertionError("Error: $message \n", 401); } // Handle curl errors if (curl_errno($ch)) { @@ -180,7 +221,7 @@ function fetchGraphQL(string $query): stdClass } throw new AssertionError("An error occurred when getting a response from GitHub.\n", 502); } - return $obj; + return $decoded; } /** @@ -201,7 +242,13 @@ function getContributionYears(string $user): array } } }"; - $response = fetchGraphQL($query); + try { + $response = fetchGraphQL($query, getGitHubToken()); + } catch (AssertionError $e) { + // retry once if an error occurred + error_log("An error occurred getting contribution years for $user: " . $e->getMessage()); + $response = fetchGraphQL($query, getGitHubToken()); + } // User not found if (!empty($response->errors)) { $type = $response->errors[0]->type ?? ""; From 4077d6edfbf2dde1b476d14d3626841a19b53153 Mon Sep 17 00:00:00 2001 From: Halim Shams <96909515+Halim-Shams@users.noreply.github.com> Date: Thu, 5 Jan 2023 22:46:01 +0430 Subject: [PATCH 19/25] feat: Added Yoruba translations and updated French (#407) Co-authored-by: Collins Oden <103268967+Collinsoden22@users.noreply.github.com> --- src/translations.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/translations.php b/src/translations.php index b01eb9ba..bdd53dd7 100644 --- a/src/translations.php +++ b/src/translations.php @@ -97,6 +97,8 @@ "Total Contributions" => "Contributions totales", "Current Streak" => "Séquence actuelle", "Longest Streak" => "Plus longue séquence", + "Week Streak" => "Séquence de la semaine", + "Longest Week Streak" => "Plus longue de semaine séquence", "Present" => "Aujourd'hui", ], "he" => [ @@ -223,6 +225,14 @@ "Longest Streak" => "Chuỗi đóng góp lớn nhất", "Present" => "Hiện tại", ], + "yo" => [ + "Total Contributions" => "Lapapọ ilowosi", + "Current Streak" => "ṣiṣan lọwọlọwọ", + "Longest Streak" => "ṣiṣan ti o gun julọ", + "Week Streak" => "ṣiṣan ọsẹ", + "Longest Week Streak" => "gunjulo ọsẹ ṣiṣan", + "Present" => "lọwọlọwọ", + ], "zh" => "zh_Hans", "zh_Hans" => [ "Total Contributions" => "合计贡献", From e3e003dcc9a2067a7ae068c5e7def23f23bcfa74 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 5 Jan 2023 18:16:30 +0000 Subject: [PATCH 20/25] docs(readme): Update translation progress --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8285e57e..76f6153a 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ To enable a theme, append `&theme=` followed by the theme name to the end of the -
en - English
English 100%
ar - العربية
العربية 100%
bg - български
български 100%
bn - বাংলা
বাংলা 100%
es - español
español 100%
fa - فارسی
فارسی 100%
he - עברית
עברית 100%
hi - हिन्दी
हिन्दी 100%
ja - 日本語
日本語 100%
ko - 한국어
한국어 100%
mr - मराठी
मराठी 100%
pl - polski
polski 100%
ps - پښتو
پښتو 100%
pt_BR - português (Brasil)
português (Brasil) 100%
ru - русский
русский 100%
uk - українська
українська 100%
zh_Hans - 中文(简体)
中文(简体) 100%
zh_Hant - 中文(繁體)
中文(繁體) 100%
da - dansk
dansk 67%
de - Deutsch
Deutsch 67%
fr - français
français 67%
id - Indonesia
Indonesia 67%
it - italiano
italiano 67%
kn - ಕನ್ನಡ
ಕನ್ನಡ 67%
nl - Nederlands
Nederlands 67%
ta - தமிழ்
தமிழ் 67%
tr - Türkçe
Türkçe 67%
vi - Tiếng Việt
Tiếng Việt 67%
+
en - English
English 100%
ar - العربية
العربية 100%
bg - български
български 100%
bn - বাংলা
বাংলা 100%
es - español
español 100%
fa - فارسی
فارسی 100%
fr - français
français 100%
he - עברית
עברית 100%
hi - हिन्दी
हिन्दी 100%
ja - 日本語
日本語 100%
ko - 한국어
한국어 100%
mr - मराठी
मराठी 100%
pl - polski
polski 100%
ps - پښتو
پښتو 100%
pt_BR - português (Brasil)
português (Brasil) 100%
ru - русский
русский 100%
uk - українська
українська 100%
yo - Èdè Yorùbá
Èdè Yorùbá 100%
zh_Hans - 中文(简体)
中文(简体) 100%
zh_Hant - 中文(繁體)
中文(繁體) 100%
da - dansk
dansk 67%
de - Deutsch
Deutsch 67%
id - Indonesia
Indonesia 67%
it - italiano
italiano 67%
kn - ಕನ್ನಡ
ಕನ್ನಡ 67%
nl - Nederlands
Nederlands 67%
ta - தமிழ்
தமிழ் 67%
tr - Türkçe
Türkçe 67%
vi - Tiếng Việt
Tiếng Việt 67%
From eb2a4c3c14f01bfc6029ce7971522b7a337c257a Mon Sep 17 00:00:00 2001 From: Jonah Lawrence Date: Thu, 5 Jan 2023 16:28:17 -0700 Subject: [PATCH 21/25] perf: Combine first two requests and refactor (#409) --- src/stats.php | 171 ++++++++++++++++++-------------------------------- 1 file changed, 61 insertions(+), 110 deletions(-) diff --git a/src/stats.php b/src/stats.php index c81fe1f7..740f0492 100644 --- a/src/stats.php +++ b/src/stats.php @@ -3,7 +3,7 @@ declare(strict_types=1); /** - * Build a query for a contribution graph + * Build a GraphQL query for a contribution graph * * @param string $user GitHub username to get graphs for * @param int $year Year to get graph for @@ -17,8 +17,8 @@ function buildContributionGraphQuery(string $user, int $year) return "query { user(login: \"$user\") { contributionsCollection(from: \"$start\", to: \"$end\") { + contributionYears contributionCalendar { - totalContributions weeks { contributionDays { contributionCount @@ -32,49 +32,60 @@ function buildContributionGraphQuery(string $user, int $year) } /** - * Get all HTTP request responses for user's contributions + * Execute multiple requests with cURL and handle GitHub API rate limits and errors * * @param string $user GitHub username to get graphs for + * @param array $years Years to get graphs for * - * @return array List of contribution graph response objects + * @return array List of GraphQL response objects */ -function getContributionGraphs(string $user): array +function executeContributionGraphRequests(string $user, array $years): array { - // Get the years the user has contributed - $contributionYears = getContributionYears($user); - // build a list of individual requests $tokens = []; $requests = []; - foreach ($contributionYears as $year) { - // create query for year - $query = buildContributionGraphQuery($user, $year); - // create curl request + // build handles for each year + foreach ($years as $year) { $tokens[$year] = getGitHubToken(); + $query = buildContributionGraphQuery($user, $year); $requests[$year] = getGraphQLCurlHandle($query, $tokens[$year]); } // build multi-curl handle $multi = curl_multi_init(); - foreach ($requests as $request) { - curl_multi_add_handle($multi, $request); + foreach ($requests as $handle) { + curl_multi_add_handle($multi, $handle); } // execute queries $running = null; do { curl_multi_exec($multi, $running); } while ($running); - // collect responses from last to first - $response = []; - foreach ($requests as $year => $request) { - $contents = curl_multi_getcontent($request); + // collect responses + $responses = []; + foreach ($requests as $year => $handle) { + $contents = curl_multi_getcontent($handle); $decoded = is_string($contents) ? json_decode($contents) : null; - // if response is empty or invalid, retry request one time - if (empty($decoded) || empty($decoded->data)) { - // if rate limit is exceeded, don't retry with same token + // if response is empty or invalid, retry request one time or throw an error + if (empty($decoded) || empty($decoded->data) || !empty($decoded->errors)) { $message = $decoded->errors[0]->message ?? ($decoded->message ?? "An API error occurred."); + $error_type = $decoded->errors[0]->type ?? ""; + // Missing SSL certificate + if (curl_errno($handle) === 60) { + throw new AssertionError("You don't have a valid SSL Certificate installed or XAMPP.", 500); + } + // Other cURL error + elseif (curl_errno($handle)) { + throw new AssertionError("cURL error: " . curl_error($handle), 500); + } + // GitHub API error - Not Found + elseif ($error_type === "NOT_FOUND") { + throw new InvalidArgumentException("Could not find a user with that name.", 404); + } + // if rate limit is exceeded, don't retry with same token if (str_contains($message, "rate limit exceeded")) { removeGitHubToken($tokens[$year]); } error_log("First attempt to decode response for $user's $year contributions failed. $message"); + // retry request $query = buildContributionGraphQuery($user, $year); $token = getGitHubToken(); $request = getGraphQLCurlHandle($query, $token); @@ -90,14 +101,36 @@ function getContributionGraphs(string $user): array continue; } } - array_unshift($response, $decoded); + $responses[$year] = $decoded; } // close the handles foreach ($requests as $request) { - curl_multi_remove_handle($multi, $request); + curl_multi_remove_handle($multi, $handle); } curl_multi_close($multi); - return $response; + return $responses; +} + +/** + * Get all HTTP request responses for user's contributions + * + * @param string $user GitHub username to get graphs for + * + * @return array List of contribution graph response objects + */ +function getContributionGraphs(string $user): array +{ + // get the list of years the user has contributed and the current year's contribution graph + $currentYear = intval(date("Y")); + $responses = executeContributionGraphRequests($user, [$currentYear]); + $contributionYears = $responses[$currentYear]->data->user->contributionsCollection->contributionYears; + // remove the current year from the list since it's already been fetched + $contributionYears = array_filter($contributionYears, function ($year) use ($currentYear) { + return $year !== $currentYear; + }); + // get the contribution graphs for the previous years + $responses += executeContributionGraphRequests($user, $contributionYears); + return $responses; } /** @@ -127,7 +160,7 @@ function getGitHubTokens() /** * Get a token from the token pool * - * @throws AssertionError if no tokens are available + * @return string GitHub token */ function getGitHubToken() { @@ -139,6 +172,8 @@ function getGitHubToken() * Remove a token from the token pool * * @param string $token Token to remove + * + * @throws AssertionError if no tokens are available after removing the token */ function removeGitHubToken(string $token) { @@ -183,94 +218,10 @@ function getGraphQLCurlHandle(string $query, string $token) return $ch; } -/** - * Create a POST request to GitHub's GraphQL API - * - * @param string $query GraphQL query - * @param string $token GitHub token to use for the request - * - * @return stdClass An object from the json response of the request - * - * @throws AssertionError If SSL verification fails - */ -function fetchGraphQL(string $query, string $token): stdClass -{ - $ch = getGraphQLCurlHandle($query, $token); - $response = curl_exec($ch); - curl_close($ch); - $decoded = is_string($response) ? json_decode($response) : null; - // handle curl errors - if ($response === false || $decoded === null || curl_getinfo($ch, CURLINFO_HTTP_CODE) >= 400) { - $message = $decoded->errors[0]->message ?? ($decoded->message ?? ""); - if (str_contains($message, "rate limit exceeded")) { - removeGitHubToken($token); - } - // set response code to curl error code - http_response_code(curl_getinfo($ch, CURLINFO_HTTP_CODE)); - // Missing SSL certificate - if (str_contains(curl_error($ch), "unable to get local issuer certificate")) { - throw new AssertionError("You don't have a valid SSL Certificate installed or XAMPP.", 400); - } - // Handle errors such as "Bad credentials" - if ($message) { - throw new AssertionError("Error: $message \n", 401); - } - // Handle curl errors - if (curl_errno($ch)) { - throw new AssertionError("cURL error: " . curl_error($ch) . "\n", 500); - } - throw new AssertionError("An error occurred when getting a response from GitHub.\n", 502); - } - return $decoded; -} - -/** - * Get the years the user has contributed - * - * @param string $user GitHub username to get years for - * - * @return array List of years the user has contributed - * - * @throws InvalidArgumentException If the user doesn't exist or there is an error - */ -function getContributionYears(string $user): array -{ - $query = "query { - user(login: \"$user\") { - contributionsCollection { - contributionYears - } - } - }"; - try { - $response = fetchGraphQL($query, getGitHubToken()); - } catch (AssertionError $e) { - // retry once if an error occurred - error_log("An error occurred getting contribution years for $user: " . $e->getMessage()); - $response = fetchGraphQL($query, getGitHubToken()); - } - // User not found - if (!empty($response->errors)) { - $type = $response->errors[0]->type ?? ""; - if ($type === "NOT_FOUND") { - throw new InvalidArgumentException("Could not find a user with that name.", 404); - } - $message = $response->errors[0]->message ?? "An API error occurred."; - // Other errors that contain a message field - throw new InvalidArgumentException($message, 502); - } - // API did not return data - if (!isset($response->data) && isset($response->message)) { - // Other errors that contain a message field - throw new InvalidArgumentException($response->message, 204); - } - return $response->data->user->contributionsCollection->contributionYears; -} - /** * Get an array of all dates with the number of contributions * - * @param array $contributionCalendars List of GraphQL response objects + * @param array $contributionCalendars List of GraphQL response objects * * @return array Y-M-D dates mapped to the number of contributions */ From c24b1afb25ba32df76d8f63635003f10ebe65cd5 Mon Sep 17 00:00:00 2001 From: Jonah Lawrence Date: Thu, 5 Jan 2023 17:02:04 -0700 Subject: [PATCH 22/25] refactor: Update types and docstrings (#410) --- src/card.php | 28 +++++++++++----------------- src/stats.php | 36 +++++++++++++++++++----------------- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/src/card.php b/src/card.php index 98a41842..52d52fd1 100644 --- a/src/card.php +++ b/src/card.php @@ -56,13 +56,13 @@ function formatDate(string $dateString, string|null $format, string $locale): st /** * Check theme and color customization parameters to generate a theme mapping * - * @param array $params Request parameters - * @return array The chosen theme or default + * @param array $params Request parameters + * @return array The chosen theme or default */ function getRequestedTheme(array $params): array { /** - * @var array> $THEMES + * @var array> $THEMES * List of theme names mapped to labeled colors */ $THEMES = include "themes.php"; @@ -125,7 +125,7 @@ function getRequestedTheme(array $params): array * than the word width. * @return string The given string wrapped at the specified length */ -function utf8WordWrap($string, $width = 75, $break = "\n", $cut_long_words = false) +function utf8WordWrap(string $string, int $width = 75, string $break = "\n", bool $cut_long_words = false): string { // match anything 1 to $width chars long followed by whitespace or EOS $string = preg_replace("/(.{1,$width})(?:\s|$)/uS", "$1$break", $string); @@ -145,7 +145,7 @@ function utf8WordWrap($string, $width = 75, $break = "\n", $cut_long_words = fal * @param string $string The input string * @return int The length of the string */ -function utf8Strlen($string) +function utf8Strlen(string $string): int { return preg_match_all("/./us", $string, $matches); } @@ -156,7 +156,6 @@ function utf8Strlen($string) * @param string $text Text to split * @param int $maxChars Maximum number of characters per line * @param int $line1Offset Offset for the first line - * * @return string Original text if one line, or split text with elements */ function splitLines(string $text, int $maxChars, int $line1Offset): string @@ -184,7 +183,6 @@ function splitLines(string $text, int $maxChars, int $line1Offset): string * Normalize a locale code * * @param string $localeCode Locale code - * * @return string Normalized locale code */ function normalizeLocaleCode(string $localeCode): string @@ -210,10 +208,9 @@ function normalizeLocaleCode(string $localeCode): string * Get the translations for a locale code after normalizing it * * @param string $localeCode Locale code - * * @return array Translations for the locale code */ -function getTranslations(string $localeCode) +function getTranslations(string $localeCode): array { // normalize locale code $localeCode = normalizeLocaleCode($localeCode); @@ -239,9 +236,8 @@ function getTranslations(string $localeCode) /** * Generate SVG output for a stats array * - * @param array $stats Streak stats - * @param array|NULL $params Request parameters - * + * @param array $stats Streak stats + * @param array|NULL $params Request parameters * @return string The generated SVG Streak Stats card * * @throws InvalidArgumentException If a locale does not exist @@ -424,8 +420,7 @@ function generateCard(array $stats, array $params = null): string * Generate SVG displaying an error message * * @param string $message The error message to display - * @param array|NULL $params Request parameters - * + * @param array|NULL $params Request parameters * @return string The generated SVG error card */ function generateErrorCard(string $message, array $params = null): string @@ -485,7 +480,6 @@ function generateErrorCard(string $message, array $params = null): string * Converts an SVG card to a PNG image * * @param string $svg The SVG for the card as a string - * * @return string The generated PNG data */ function convertSvgToPng(string $svg): string @@ -516,7 +510,7 @@ function convertSvgToPng(string $svg): string if (empty($png)) { // `2>&1`: redirect stderr to stdout $error = shell_exec("$cmd 2>&1"); // skipcq: PHP-A1009 - throw new Exception("Failed to convert SVG to PNG: {$error}", 500); + throw new InvalidArgumentException("Failed to convert SVG to PNG: {$error}", 500); } // return the generated png @@ -527,7 +521,6 @@ function convertSvgToPng(string $svg): string * Return headers and response based on type * * @param string|array $output The stats (array) or error message (string) to display - * * @return array The Content-Type header and the response body, and status code in case of an error */ function generateOutput(string|array $output): array @@ -573,6 +566,7 @@ function generateOutput(string|array $output): array * * @param string|array $output The Content-Type header and the response body * @param int $responseCode The HTTP response code to send + * @return void The function exits after sending the response */ function renderOutput(string|array $output, int $responseCode = 200): void { diff --git a/src/stats.php b/src/stats.php index 740f0492..73bac082 100644 --- a/src/stats.php +++ b/src/stats.php @@ -7,10 +7,9 @@ * * @param string $user GitHub username to get graphs for * @param int $year Year to get graph for - * * @return string GraphQL query */ -function buildContributionGraphQuery(string $user, int $year) +function buildContributionGraphQuery(string $user, int $year): string { $start = "$year-01-01T00:00:00Z"; $end = "$year-12-31T23:59:59Z"; @@ -36,8 +35,7 @@ function buildContributionGraphQuery(string $user, int $year) * * @param string $user GitHub username to get graphs for * @param array $years Years to get graphs for - * - * @return array List of GraphQL response objects + * @return array List of GraphQL response objects with years as keys */ function executeContributionGraphRequests(string $user, array $years): array { @@ -115,7 +113,6 @@ function executeContributionGraphRequests(string $user, array $years): array * Get all HTTP request responses for user's contributions * * @param string $user GitHub username to get graphs for - * * @return array List of contribution graph response objects */ function getContributionGraphs(string $user): array @@ -138,7 +135,7 @@ function getContributionGraphs(string $user): array * * @return array List of tokens */ -function getGitHubTokens() +function getGitHubTokens(): array { // result is already calculated if (isset($GLOBALS["ALL_TOKENS"])) { @@ -161,10 +158,16 @@ function getGitHubTokens() * Get a token from the token pool * * @return string GitHub token + * + * @throws AssertionError if no tokens are available */ -function getGitHubToken() +function getGitHubToken(): string { $all_tokens = getGitHubTokens(); + // if there is no available token, throw an error (this should never happen) + if (empty($all_tokens)) { + throw new AssertionError("There is no GitHub token available.", 500); + } return $all_tokens[array_rand($all_tokens)]; } @@ -172,10 +175,11 @@ function getGitHubToken() * Remove a token from the token pool * * @param string $token Token to remove + * @return void * * @throws AssertionError if no tokens are available after removing the token */ -function removeGitHubToken(string $token) +function removeGitHubToken(string $token): void { $index = array_search($token, $GLOBALS["ALL_TOKENS"]); if ($index !== false) { @@ -194,10 +198,9 @@ function removeGitHubToken(string $token) * * @param string $query GraphQL query * @param string $token GitHub token to use for the request - * * @return CurlHandle The curl handle for the request */ -function getGraphQLCurlHandle(string $query, string $token) +function getGraphQLCurlHandle(string $query, string $token): CurlHandle { $headers = [ "Authorization: bearer $token", @@ -221,9 +224,8 @@ function getGraphQLCurlHandle(string $query, string $token) /** * Get an array of all dates with the number of contributions * - * @param array $contributionCalendars List of GraphQL response objects - * - * @return array Y-M-D dates mapped to the number of contributions + * @param array $contributionCalendars List of GraphQL response objects by year + * @return array Y-M-D dates mapped to the number of contributions */ function getContributionDates(array $contributionGraphs): array { @@ -253,8 +255,8 @@ function getContributionDates(array $contributionGraphs): array /** * Get a stats array with the contribution count, daily streak, and dates * - * @param array $contributions Y-M-D contribution dates with contribution counts - * @return array Streak stats + * @param array $contributions Y-M-D contribution dates with contribution counts + * @return array Streak stats */ function getContributionStats(array $contributions): array { @@ -331,8 +333,8 @@ function getPreviousSunday(string $date): string /** * Get a stats array with the contribution count, weekly streak, and dates * - * @param array $contributions Y-M-D contribution dates with contribution counts - * @return array Streak stats + * @param array $contributions Y-M-D contribution dates with contribution counts + * @return array Streak stats */ function getWeeklyContributionStats(array $contributions): array { From 84fb6f518db2955873afc846be10095a366a4851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Gangloff?= Date: Wed, 11 Jan 2023 18:59:33 +0100 Subject: [PATCH 23/25] fix: Updated French translation of `Longest Week Streak` (#413) --- src/translations.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations.php b/src/translations.php index bdd53dd7..6c793aeb 100644 --- a/src/translations.php +++ b/src/translations.php @@ -98,7 +98,7 @@ "Current Streak" => "Séquence actuelle", "Longest Streak" => "Plus longue séquence", "Week Streak" => "Séquence de la semaine", - "Longest Week Streak" => "Plus longue de semaine séquence", + "Longest Week Streak" => "Plus longue séquence hebdomadaire", "Present" => "Aujourd'hui", ], "he" => [ From 2c9f427a7360e5ffc2a0fab95aad1a568f49ad62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jes=C3=BAs=20Alejo=20Sillero?= <122260827+JuanJesusAlejoSillero@users.noreply.github.com> Date: Fri, 13 Jan 2023 01:41:48 +0000 Subject: [PATCH 24/25] fix: Updated Spanish translation (#414) --- src/translations.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations.php b/src/translations.php index 6c793aeb..26ec3f2d 100644 --- a/src/translations.php +++ b/src/translations.php @@ -77,7 +77,7 @@ "Present" => "Heute", ], "es" => [ - "Total Contributions" => "Todas Contribuciones", + "Total Contributions" => "Contribuciones Totales", "Current Streak" => "Racha Actual", "Longest Streak" => "Racha Más Larga", "Week Streak" => "Racha Semanal", From 0fd28d17e23aad1e000f3b302a9ba87c8d684cc7 Mon Sep 17 00:00:00 2001 From: Jonah Lawrence Date: Thu, 12 Jan 2023 18:52:22 -0700 Subject: [PATCH 25/25] chore: Bump version to 0.26.0 --- composer.json | 2 +- composer.lock | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/composer.json b/composer.json index 1a0fa13a..bf460e42 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ "stats" ], "license": "MIT", - "version": "0.25.0", + "version": "0.26.0", "homepage": "https://github.com/DenverCoder1/github-readme-streak-stats", "autoload": { "classmap": [ diff --git a/composer.lock b/composer.lock index 1de25444..e7195848 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dafd149b850dfcef2b8cdec081c5410e", + "content-hash": "5c08d15859f1b257e2ff7475b6fab683", "packages": [ { "name": "graham-campbell/result-type", @@ -479,30 +479,30 @@ "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.4.1", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9", + "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^0.16 || ^1", "phpstan/phpstan": "^1.4", "phpstan/phpstan-phpunit": "^1", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.22" + "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", "autoload": { @@ -529,7 +529,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, "funding": [ { @@ -545,7 +545,7 @@ "type": "tidelift" } ], - "time": "2022-03-03T08:28:38+00:00" + "time": "2022-12-30T00:15:36+00:00" }, { "name": "myclabs/deep-copy", @@ -775,16 +775,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.22", + "version": "9.2.23", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "e4bf60d2220b4baaa0572986b5d69870226b06df" + "reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e4bf60d2220b4baaa0572986b5d69870226b06df", - "reference": "e4bf60d2220b4baaa0572986b5d69870226b06df", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c", + "reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c", "shasum": "" }, "require": { @@ -840,7 +840,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.22" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.23" }, "funding": [ { @@ -848,7 +848,7 @@ "type": "github" } ], - "time": "2022-12-18T16:40:55+00:00" + "time": "2022-12-28T12:41:10+00:00" }, { "name": "phpunit/php-file-iterator",