From 7f0097bf82beb52883e0795368daec7d0f91d812 Mon Sep 17 00:00:00 2001 From: Aliothmoon Date: Mon, 6 Apr 2026 00:34:17 +0800 Subject: [PATCH 1/3] ci: add scheduled workflow to sync QQ group info from index.json --- .github/workflows/sync-qq-groups.yml | 59 ++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/sync-qq-groups.yml diff --git a/.github/workflows/sync-qq-groups.yml b/.github/workflows/sync-qq-groups.yml new file mode 100644 index 0000000..c54eade --- /dev/null +++ b/.github/workflows/sync-qq-groups.yml @@ -0,0 +1,59 @@ +name: Sync QQ Groups + +on: + schedule: + - cron: "0 */6 * * *" + workflow_dispatch: + +permissions: + contents: write + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + show-progress: false + + - name: Fetch index.json and update constants + run: | + # 拉取远程数据 + DATA=$(curl -sf https://end.maafw.com/index.json) || exit 0 + + # 解析字段 + USER_GROUP=$(echo "$DATA" | jq -r '.qq_groups.user.number // empty') + USER_LINK=$(echo "$DATA" | jq -r '.qq_groups.user.link // empty') + DEV_GROUP=$(echo "$DATA" | jq -r '.qq_groups.dev.number // empty') + DEV_LINK=$(echo "$DATA" | jq -r '.qq_groups.dev.link // empty') + + # 任一字段为空则跳过 + if [ -z "$USER_GROUP" ] || [ -z "$USER_LINK" ] || [ -z "$DEV_GROUP" ] || [ -z "$DEV_LINK" ]; then + echo "Missing fields in index.json, skipping" + exit 0 + fi + + # 替换 constants.ts 中的值 + FILE="app/constants.ts" + sed -i "s|USER_GROUP: \".*\"|USER_GROUP: \"$USER_GROUP\"|" "$FILE" + sed -i "s|USER_GROUP_LINK: \".*\"|USER_GROUP_LINK: \"$USER_LINK\"|" "$FILE" + sed -i "s|DEV_GROUP: \".*\"|DEV_GROUP: \"$DEV_GROUP\"|" "$FILE" + sed -i "s|DEV_GROUP_LINK: \".*\"|DEV_GROUP_LINK: \"$DEV_LINK\"|" "$FILE" + + # 同步 README.md + sed -i "s|\[用户 QQ 群\](https://qm.qq.com/q/[^)]*): [0-9]*|[用户 QQ 群]($USER_LINK): $USER_GROUP|" README.md + sed -i "s|\[开发 QQ 群\](https://qm.qq.com/q/[^)]*): [0-9]*|[开发 QQ 群]($DEV_LINK): $DEV_GROUP|" README.md + + - name: Check for changes + id: diff + run: | + git diff --quiet && echo "changed=false" >> "$GITHUB_OUTPUT" || echo "changed=true" >> "$GITHUB_OUTPUT" + + - name: Commit and push + if: steps.diff.outputs.changed == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add app/constants.ts README.md + git commit -m "chore: sync QQ group info from index.json" + git push From 949ca736b7ed3828f9407ebf6e9bf11873c3f2e8 Mon Sep 17 00:00:00 2001 From: Aliothmoon Date: Mon, 6 Apr 2026 00:56:54 +0800 Subject: [PATCH 2/3] ci: change trigger type --- .github/workflows/sync-qq-groups.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sync-qq-groups.yml b/.github/workflows/sync-qq-groups.yml index c54eade..7fe6a67 100644 --- a/.github/workflows/sync-qq-groups.yml +++ b/.github/workflows/sync-qq-groups.yml @@ -1,8 +1,8 @@ name: Sync QQ Groups on: - schedule: - - cron: "0 */6 * * *" + repository_dispatch: + types: [sync-qq-groups] workflow_dispatch: permissions: @@ -14,6 +14,7 @@ jobs: steps: - uses: actions/checkout@v5 with: + token: ${{ secrets.PAT_TOKEN }} show-progress: false - name: Fetch index.json and update constants From ba433f29fa8543645b4cbb4dbfdafe84c41cf2de Mon Sep 17 00:00:00 2001 From: Aliothmoon Date: Mon, 6 Apr 2026 01:24:37 +0800 Subject: [PATCH 3/3] feat: dynamically fetch QQ group info from API with cache --- .github/workflows/sync-qq-groups.yml | 17 ++---- app/components/Footer.tsx | 12 +++-- app/components/Header.tsx | 6 ++- app/hooks/useQQGroups.ts | 78 ++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 21 deletions(-) create mode 100644 app/hooks/useQQGroups.ts diff --git a/.github/workflows/sync-qq-groups.yml b/.github/workflows/sync-qq-groups.yml index 7fe6a67..689bc66 100644 --- a/.github/workflows/sync-qq-groups.yml +++ b/.github/workflows/sync-qq-groups.yml @@ -17,31 +17,20 @@ jobs: token: ${{ secrets.PAT_TOKEN }} show-progress: false - - name: Fetch index.json and update constants + - name: Fetch index.json and update README run: | - # 拉取远程数据 DATA=$(curl -sf https://end.maafw.com/index.json) || exit 0 - # 解析字段 USER_GROUP=$(echo "$DATA" | jq -r '.qq_groups.user.number // empty') USER_LINK=$(echo "$DATA" | jq -r '.qq_groups.user.link // empty') DEV_GROUP=$(echo "$DATA" | jq -r '.qq_groups.dev.number // empty') DEV_LINK=$(echo "$DATA" | jq -r '.qq_groups.dev.link // empty') - # 任一字段为空则跳过 if [ -z "$USER_GROUP" ] || [ -z "$USER_LINK" ] || [ -z "$DEV_GROUP" ] || [ -z "$DEV_LINK" ]; then echo "Missing fields in index.json, skipping" exit 0 fi - # 替换 constants.ts 中的值 - FILE="app/constants.ts" - sed -i "s|USER_GROUP: \".*\"|USER_GROUP: \"$USER_GROUP\"|" "$FILE" - sed -i "s|USER_GROUP_LINK: \".*\"|USER_GROUP_LINK: \"$USER_LINK\"|" "$FILE" - sed -i "s|DEV_GROUP: \".*\"|DEV_GROUP: \"$DEV_GROUP\"|" "$FILE" - sed -i "s|DEV_GROUP_LINK: \".*\"|DEV_GROUP_LINK: \"$DEV_LINK\"|" "$FILE" - - # 同步 README.md sed -i "s|\[用户 QQ 群\](https://qm.qq.com/q/[^)]*): [0-9]*|[用户 QQ 群]($USER_LINK): $USER_GROUP|" README.md sed -i "s|\[开发 QQ 群\](https://qm.qq.com/q/[^)]*): [0-9]*|[开发 QQ 群]($DEV_LINK): $DEV_GROUP|" README.md @@ -55,6 +44,6 @@ jobs: run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git add app/constants.ts README.md - git commit -m "chore: sync QQ group info from index.json" + git add README.md + git commit -m "chore: sync QQ group info in README" git push diff --git a/app/components/Footer.tsx b/app/components/Footer.tsx index 7b0d82f..2ba1aff 100644 --- a/app/components/Footer.tsx +++ b/app/components/Footer.tsx @@ -3,10 +3,12 @@ import { motion } from "framer-motion"; import { useTranslation } from "react-i18next"; import Image from "next/image"; -import { FRIEND_LINKS, GITHUB_URLS, QQ_GROUPS } from "../constants"; +import { FRIEND_LINKS, GITHUB_URLS } from "../constants"; +import { useQQGroups } from "../hooks/useQQGroups"; export default function Footer() { const { t } = useTranslation(); + const qqGroups = useQQGroups(); // 性能优化:使用固定速度,移除滚动速度检测 const marqueeDuration = 200; @@ -51,22 +53,22 @@ export default function Footer() { diff --git a/app/components/Header.tsx b/app/components/Header.tsx index 2fe8138..2e75cc5 100644 --- a/app/components/Header.tsx +++ b/app/components/Header.tsx @@ -7,10 +7,12 @@ import { Book, Languages, Users } from "lucide-react"; import { Button } from "./ui/Button"; import { useTranslation } from "react-i18next"; import { ThemeToggle } from "./ThemeToggle"; -import { GITHUB_URLS, QQ_GROUPS } from "../constants"; +import { GITHUB_URLS } from "../constants"; +import { useQQGroups } from "../hooks/useQQGroups"; export default function Header() { const { t, i18n } = useTranslation(); + const qqGroups = useQQGroups(); const toggleLanguage = () => { const newLang = i18n.language === "zh" ? "en" : "zh"; @@ -52,7 +54,7 @@ export default function Header() { {t("header.docs")} CACHE_TTL) return null; + return data; + } catch { + return null; + } +} + +function writeCache(data: QQGroups) { + try { + localStorage.setItem(CACHE_KEY, JSON.stringify({ data, ts: Date.now() })); + } catch { + // storage full or unavailable + } +} + +export function useQQGroups() { + const [groups, setGroups] = useState(fallback); + + const fetchGroups = useCallback(async () => { + const cached = readCache(); + if (cached) { + setGroups(cached); + return; + } + try { + const res = await fetch("https://end.maafw.com/index.json"); + if (!res.ok) return; + const data = await res.json(); + const qq = data.qq_groups; + if ( + qq?.user?.number && + qq?.user?.link && + qq?.dev?.number && + qq?.dev?.link + ) { + const result: QQGroups = { user: qq.user, dev: qq.dev }; + setGroups(result); + writeCache(result); + } + } catch { + // fallback to constants + } + }, []); + + useEffect(() => { + (async () => { + await fetchGroups(); + })(); + }, [fetchGroups]); + + return groups; +}