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", pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy