Skip to content

Commit 0f9e2c3

Browse files
authored
ci: add scheduled workflow to sync QQ group info from index.json (#28)
## 由 Sourcery 生成的摘要 CI: - 引入一个可定时执行且可手动触发的 GitHub Actions 工作流,用于从远程的 `index.json` 获取 QQ 群信息,更新 `app/constants.ts` 和 `README.md`,并自动提交所有变更。 <details> <summary>Original summary in English</summary> ## Summary by Sourcery CI: - 引入一个可手动且可外部触发的 GitHub Actions 工作流,用于从远程的 `index.json` 获取 QQ 群数据,相应地更新 `app/constants.ts` 和 `README.md`,并自动提交所有变更。 <details> <summary>Original summary in English</summary> ## Summary by Sourcery CI: - Introduce a manually and externally triggerable GitHub Actions workflow that fetches QQ group data from a remote index.json, updates app/constants.ts and README.md accordingly, and auto-commits any changes. </details> </details>
1 parent 2289656 commit 0f9e2c3

4 files changed

Lines changed: 138 additions & 7 deletions

File tree

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Sync QQ Groups
2+
3+
on:
4+
repository_dispatch:
5+
types: [sync-qq-groups]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
sync:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v5
16+
with:
17+
token: ${{ secrets.PAT_TOKEN }}
18+
show-progress: false
19+
20+
- name: Fetch index.json and update README
21+
run: |
22+
DATA=$(curl -sf https://end.maafw.com/index.json) || exit 0
23+
24+
USER_GROUP=$(echo "$DATA" | jq -r '.qq_groups.user.number // empty')
25+
USER_LINK=$(echo "$DATA" | jq -r '.qq_groups.user.link // empty')
26+
DEV_GROUP=$(echo "$DATA" | jq -r '.qq_groups.dev.number // empty')
27+
DEV_LINK=$(echo "$DATA" | jq -r '.qq_groups.dev.link // empty')
28+
29+
if [ -z "$USER_GROUP" ] || [ -z "$USER_LINK" ] || [ -z "$DEV_GROUP" ] || [ -z "$DEV_LINK" ]; then
30+
echo "Missing fields in index.json, skipping"
31+
exit 0
32+
fi
33+
34+
sed -i "s|\[用户 QQ 群\](https://qm.qq.com/q/[^)]*): [0-9]*|[用户 QQ 群]($USER_LINK): $USER_GROUP|" README.md
35+
sed -i "s|\[开发 QQ 群\](https://qm.qq.com/q/[^)]*): [0-9]*|[开发 QQ 群]($DEV_LINK): $DEV_GROUP|" README.md
36+
37+
- name: Check for changes
38+
id: diff
39+
run: |
40+
git diff --quiet && echo "changed=false" >> "$GITHUB_OUTPUT" || echo "changed=true" >> "$GITHUB_OUTPUT"
41+
42+
- name: Commit and push
43+
if: steps.diff.outputs.changed == 'true'
44+
run: |
45+
git config user.name "github-actions[bot]"
46+
git config user.email "github-actions[bot]@users.noreply.github.com"
47+
git add README.md
48+
git commit -m "chore: sync QQ group info in README"
49+
git push

app/components/Footer.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
import { motion } from "framer-motion";
44
import { useTranslation } from "react-i18next";
55
import Image from "next/image";
6-
import { FRIEND_LINKS, GITHUB_URLS, QQ_GROUPS } from "../constants";
6+
import { FRIEND_LINKS, GITHUB_URLS } from "../constants";
7+
import { useQQGroups } from "../hooks/useQQGroups";
78

89
export default function Footer() {
910
const { t } = useTranslation();
11+
const qqGroups = useQQGroups();
1012

1113
// 性能优化:使用固定速度,移除滚动速度检测
1214
const marqueeDuration = 200;
@@ -51,22 +53,22 @@ export default function Footer() {
5153
<ul className="space-y-2 text-sm text-black/80 dark:text-white/70">
5254
<li>
5355
<a
54-
href={QQ_GROUPS.USER_GROUP_LINK}
56+
href={qqGroups.user.link}
5557
target="_blank"
5658
rel="noopener noreferrer"
5759
className="transition-colors hover:text-[#c49102] dark:hover:text-[#FFE600]"
5860
>
59-
{t("footer.userGroup")}: {QQ_GROUPS.USER_GROUP}
61+
{t("footer.userGroup")}: {qqGroups.user.number}
6062
</a>
6163
</li>
6264
<li>
6365
<a
64-
href={QQ_GROUPS.DEV_GROUP_LINK}
66+
href={qqGroups.dev.link}
6567
target="_blank"
6668
rel="noopener noreferrer"
6769
className="transition-colors hover:text-[#c49102] dark:hover:text-[#FFE600]"
6870
>
69-
{t("footer.devGroup")}: {QQ_GROUPS.DEV_GROUP}
71+
{t("footer.devGroup")}: {qqGroups.dev.number}
7072
</a>
7173
</li>
7274
</ul>

app/components/Header.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import { Book, Languages, Users } from "lucide-react";
77
import { Button } from "./ui/Button";
88
import { useTranslation } from "react-i18next";
99
import { ThemeToggle } from "./ThemeToggle";
10-
import { GITHUB_URLS, QQ_GROUPS } from "../constants";
10+
import { GITHUB_URLS } from "../constants";
11+
import { useQQGroups } from "../hooks/useQQGroups";
1112

1213
export default function Header() {
1314
const { t, i18n } = useTranslation();
15+
const qqGroups = useQQGroups();
1416

1517
const toggleLanguage = () => {
1618
const newLang = i18n.language === "zh" ? "en" : "zh";
@@ -52,7 +54,7 @@ export default function Header() {
5254
<Book size={16} /> {t("header.docs")}
5355
</Link>
5456
<Link
55-
href={QQ_GROUPS.USER_GROUP_LINK}
57+
href={qqGroups.user.link}
5658
target="_blank"
5759
rel="noopener noreferrer"
5860
className="flex items-center gap-2 font-mono text-sm text-black/80 transition-colors hover:text-[#c49102] dark:text-white/80 dark:hover:text-[#FFE600]"

app/hooks/useQQGroups.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { useCallback, useEffect, useState } from "react";
2+
import { QQ_GROUPS } from "../constants";
3+
4+
interface QQGroup {
5+
number: string;
6+
link: string;
7+
}
8+
9+
interface QQGroups {
10+
user: QQGroup;
11+
dev: QQGroup;
12+
}
13+
14+
const CACHE_KEY = "maaend-qq-groups";
15+
const CACHE_TTL = 1000 * 60 * 60; // 1 hour
16+
17+
const fallback: QQGroups = {
18+
user: { number: QQ_GROUPS.USER_GROUP, link: QQ_GROUPS.USER_GROUP_LINK },
19+
dev: { number: QQ_GROUPS.DEV_GROUP, link: QQ_GROUPS.DEV_GROUP_LINK },
20+
};
21+
22+
function readCache(): QQGroups | null {
23+
try {
24+
const raw = localStorage.getItem(CACHE_KEY);
25+
if (!raw) return null;
26+
const { data, ts } = JSON.parse(raw);
27+
if (Date.now() - ts > CACHE_TTL) return null;
28+
return data;
29+
} catch {
30+
return null;
31+
}
32+
}
33+
34+
function writeCache(data: QQGroups) {
35+
try {
36+
localStorage.setItem(CACHE_KEY, JSON.stringify({ data, ts: Date.now() }));
37+
} catch {
38+
// storage full or unavailable
39+
}
40+
}
41+
42+
export function useQQGroups() {
43+
const [groups, setGroups] = useState<QQGroups>(fallback);
44+
45+
const fetchGroups = useCallback(async () => {
46+
const cached = readCache();
47+
if (cached) {
48+
setGroups(cached);
49+
return;
50+
}
51+
try {
52+
const res = await fetch("https://end.maafw.com/index.json");
53+
if (!res.ok) return;
54+
const data = await res.json();
55+
const qq = data.qq_groups;
56+
if (
57+
qq?.user?.number &&
58+
qq?.user?.link &&
59+
qq?.dev?.number &&
60+
qq?.dev?.link
61+
) {
62+
const result: QQGroups = { user: qq.user, dev: qq.dev };
63+
setGroups(result);
64+
writeCache(result);
65+
}
66+
} catch {
67+
// fallback to constants
68+
}
69+
}, []);
70+
71+
useEffect(() => {
72+
(async () => {
73+
await fetchGroups();
74+
})();
75+
}, [fetchGroups]);
76+
77+
return groups;
78+
}

0 commit comments

Comments
 (0)