-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
314 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import {ref} from "vue"; | ||
|
||
import {useData} from "vitepress"; | ||
|
||
const i18nData = { | ||
en: { | ||
stats: 'Stats', | ||
online: 'Online', | ||
offline: 'Offline', | ||
total: 'Total', | ||
fetching: 'Fetching', | ||
stars: 'Stars', | ||
forks: 'Forks', | ||
issues: 'Issues', | ||
prs: 'Pull Requests', | ||
visitors: 'Visitor', | ||
size: 'Size', | ||
plugins: 'Plugins', | ||
resources: 'Resources', | ||
pluginStore: 'Plugin Store', | ||
pluginStoreDesc: 'Content from the LightSnow Plugin Store, LightSnow supports NoneBot through the lpnonebot plugin, and references some NoneBot plugins', | ||
liteyukiOnly: 'Liteyuki Only', | ||
search: 'Search', | ||
resourceStore: 'Resources Store', | ||
}, | ||
zh: { | ||
stats: '统计信息', | ||
online: '在线', | ||
offline: '离线', | ||
total: '实例', | ||
fetching: '获取中', | ||
stars: '星标', | ||
forks: '分叉', | ||
issues: '开启议题', | ||
prs: '合并请求', | ||
visitors: '访客', | ||
size: '大小', | ||
plugins: '插件', | ||
resources: '主题资源', | ||
store: '商店', | ||
pluginStore: '插件商店', | ||
pluginStoreDesc: '内容来自轻雪插件商店,轻雪通过lpnonebot插件对NoneBot实现支持,引用了部分NoneBot插件', | ||
liteyukiOnly: '仅轻雪', | ||
search: '搜索', | ||
resourceStore: '资源商店', | ||
} | ||
} | ||
|
||
let refData = {} | ||
|
||
function getText(lang: string, key: string): string { | ||
lang = formatLang(lang); | ||
return i18nData[lang][key]; | ||
} | ||
|
||
function formatLang(lang: string): string { | ||
if (lang.includes('-')) { | ||
return lang.split('-')[0]; | ||
} | ||
return lang; | ||
} | ||
|
||
export function updateRefData() { | ||
const lang = formatLang(useData().site.value.lang); | ||
for (let key in refData) { | ||
refData[key].value = getText(lang, key); | ||
} | ||
} | ||
|
||
export function getTextRef(key: string): any { | ||
const lang = formatLang(useData().site.value.lang); | ||
refData[key] = getText(lang, key); | ||
return refData[key] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
// URL | ||
export const OWNER = "LiteyukiStudio" | ||
export const REPO = "LiteyukiBot" | ||
const githubAPIUrl = "https://api.github.com" | ||
const giteaAPIUrl = "https://git.liteyuki.icu/api/v1" | ||
const onlineFetchUrl = "https://api.liteyuki.icu/online"; | ||
const totalFetchUrl = "https://api.liteyuki.icu/count"; | ||
const visitRecordUrl = "https://api.liteyuki.icu/visit"; | ||
const visitCountUrl = "https://api.liteyuki.icu/visit_count"; | ||
const visitCurrentCountUrl = "https://api.liteyuki.icu/visit_index"; | ||
|
||
export const RepoUrl = `https://github.com/${OWNER}/${REPO}` | ||
export const StarMapUrl = "https://starmap.liteyuki.icu" | ||
|
||
type GithubStats = { | ||
stars: number; | ||
forks: number; | ||
watchers: number; | ||
issues?: number; | ||
prs?: number; | ||
size?: number; | ||
} | ||
|
||
// 异步接口 | ||
interface StatsApi { | ||
getTotal: () => Promise<number>; | ||
getOnline: () => Promise<number>; | ||
getGithubStats: () => Promise<GithubStats>; | ||
getPluginNum: () => Promise<number>; | ||
getResourceNum: () => Promise<number>; | ||
getVisitCount: () => Promise<number>; | ||
getCurrentVisitCount: () => Promise<number>; | ||
} | ||
|
||
|
||
export type {GithubStats}; | ||
|
||
async function getGiteaStats() { | ||
try { | ||
const url = `${giteaAPIUrl}/repos/${OWNER}/${REPO}`; | ||
console.log(url); | ||
const res = await fetch(url); | ||
const data = await res.json(); | ||
return { | ||
stars: data.stars_count, | ||
forks: data.forks_count, | ||
watchers: data.watchers_count, | ||
issues: 0, | ||
prs: 0, | ||
size: data.size, | ||
}; | ||
} catch (e) { | ||
return { | ||
stars: -1, | ||
forks: -1, | ||
watchers: -1, | ||
issues: -1, | ||
prs: -1, | ||
size: -1, | ||
}; | ||
} | ||
} | ||
|
||
async function getGithubStats() { | ||
try { | ||
const res = await fetch(`${githubAPIUrl}/repos/${OWNER}/${REPO}`); | ||
const data = await res.json(); | ||
return { | ||
stars: data.stargazers_count, | ||
forks: data.forks_count, | ||
watchers: data.watchers_count, | ||
issues: data.open_issues_count, | ||
prs: data.open_issues_count, | ||
size: data.size, | ||
}; | ||
} catch (e) { | ||
return { | ||
stars: -1, | ||
forks: -1, | ||
watchers: -1, | ||
issues: -1, | ||
prs: -1, | ||
size: -1, | ||
}; | ||
} | ||
} | ||
|
||
async function getRepoStats() { | ||
// 两个接口各数据,加和返回 | ||
const githubStats = await getGithubStats(); | ||
const giteaStats = await getGiteaStats(); | ||
return { | ||
stars: githubStats.stars + giteaStats.stars, | ||
forks: githubStats.forks + giteaStats.forks, | ||
watchers: githubStats.watchers + giteaStats.watchers, | ||
issues: githubStats.issues + giteaStats.issues, | ||
prs: githubStats.prs + giteaStats.prs, | ||
size: githubStats.size + giteaStats.size, | ||
}; | ||
} | ||
|
||
// 实现接口 | ||
export const statsApi: StatsApi = { | ||
getTotal: async () => { | ||
try { | ||
const res = await fetch(totalFetchUrl); | ||
const data = await res.json(); | ||
return data.register; | ||
} catch (e) { | ||
return -1; | ||
} | ||
}, | ||
getOnline: async () => { | ||
try { | ||
const res = await fetch(onlineFetchUrl); | ||
const data = await res.json(); | ||
return data.online; | ||
} catch (e) { | ||
return -1; | ||
} | ||
}, | ||
getGithubStats: getRepoStats, | ||
getPluginNum: async () => { | ||
try { | ||
const res = await fetch('/plugins.json'); | ||
const data = await res.json(); | ||
return data.length; | ||
} catch (e) { | ||
return -1; | ||
} | ||
}, | ||
getResourceNum: async () => { | ||
try { | ||
const res = await fetch('/resources.json'); | ||
const data = await res.json(); | ||
return data.length; | ||
} catch (e) { | ||
return -1; | ||
} | ||
}, | ||
getVisitCount: async () => { | ||
try { | ||
const res = await fetch(visitCountUrl); | ||
const data = await res.json(); | ||
return data.count; | ||
} catch (e) { | ||
return -1; | ||
} | ||
}, | ||
getCurrentVisitCount: async () => { | ||
try { | ||
// 带device_id post | ||
const res = await fetch(visitCurrentCountUrl, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/x-www-form-urlencoded' | ||
}, | ||
body: new URLSearchParams({'device_id': getDeviceId()}).toString(), | ||
}); | ||
const data = await res.json(); | ||
return data.index; | ||
} catch (e) { | ||
return -1; | ||
} | ||
}, | ||
}; | ||
|
||
function getDeviceId(): string { | ||
// 用户每次访问时生成一个唯一的设备ID,储存在localStorage中,用于统计用户数量 | ||
const deviceIdKey = 'deviceId'; | ||
let deviceId = localStorage.getItem(deviceIdKey); | ||
|
||
if (!deviceId) { | ||
deviceId = generateUUID(); | ||
localStorage.setItem(deviceIdKey, deviceId); | ||
} | ||
return deviceId; | ||
} | ||
|
||
export async function uploadVisitRecord() { | ||
const deviceId = getDeviceId(); | ||
try { | ||
await fetch(visitRecordUrl, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/x-www-form-urlencoded' | ||
}, | ||
body: new URLSearchParams({'device_id': deviceId}).toString(), | ||
}); | ||
} catch (e) { | ||
console.error('Failed to upload visit record:', e); | ||
} | ||
} | ||
|
||
function generateUUID(): string { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||
var r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); | ||
return v.toString(16); | ||
}); | ||
} |