diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 461a2c4..53b26c7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,10 +25,15 @@ jobs: run: bun run format env: OPENAI_TOKEN: ${{ secrets.OPENAI_TOKEN }} + + - name: Awesome + run: bun run awesome - name: Prettier run: | - echo "module.exports = require('@lobehub/lint').prettier;" >> .prettierrc.js + echo "module.exports = require('@lobehub/lint').remarklint;" >> .remarkrc.cjs + bun run lint:md + echo "module.exports = require('@lobehub/lint').prettier;" >> .prettierrc.cjs bun run prettier - name: Commit changes diff --git a/.i18nrc.js b/.i18nrc.js index 2392ecd..d21c076 100644 --- a/.i18nrc.js +++ b/.i18nrc.js @@ -2,4 +2,5 @@ module.exports = { selectors: ['meta.title', 'meta.description', 'meta.tags'], entryLocale: 'en-US', outputLocales: ['zh-CN', 'ru-RU'], + // outputLocales: ['zh-CN', 'zh-TW', 'ru-RU', 'ja-JP', 'ko-KR'], }; diff --git a/README.md b/README.md index ec9764e..1ad343f 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,65 @@ If you wish to add a plugin onto the index, make an entry in `plugins` directory + +## 🕶 Awesome Plugins + + + +### Website Crawler + +By **[@LobeHub](https://github.com/lobehub/chat-plugin-web-crawler)** on **2023-08-17** + +Extract content from web links + +`web` `content-crawler` + +
+ +[![][back-to-top]](#readme-top) + +
+ +--- + +### Search Engine + +By **[@LobeHub](https://github.com/lobehub/chat-plugin-search-engine)** on **2023-08-15** + +Query search engine to get information + +`web` `search` + +
+ +[![][back-to-top]](#readme-top) + +
+ +--- + +### Realtime Weather + +By **[@LobeHub](https://github.com/lobehub/chat-plugin-realtime-weather)** on **2023-08-12** + +Get realtime weather information + +`weather` `realtime` + +
+ +[![][back-to-top]](#readme-top) + +
+ + + +
+ +[![][back-to-top]](#readme-top) + +
+ ## 🛳 Self Hosting If you want to deploy this service by yourself, you can follow the steps below. diff --git a/README.zh-CN.md b/README.zh-CN.md index 5713303..b8e8317 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -72,6 +72,64 @@ +## 🕶 Awesome Plugins + + + +### 网站爬虫 + +By **[@LobeHub](https://github.com/lobehub/chat-plugin-web-crawler)** on **2023-08-17** + +从网页链接中提取内容 + +`网页` `内容爬取器` + +
+ +[![][back-to-top]](#readme-top) + +
+ +--- + +### 搜索引擎 + +By **[@LobeHub](https://github.com/lobehub/chat-plugin-search-engine)** on **2023-08-15** + +查询搜索引擎以获取信息 + +`网络` `搜索` + +
+ +[![][back-to-top]](#readme-top) + +
+ +--- + +### 实时天气 + +By **[@LobeHub](https://github.com/lobehub/chat-plugin-realtime-weather)** on **2023-08-12** + +获取实时天气信息 + +`天气` `实时` + +
+ +[![][back-to-top]](#readme-top) + +
+ + + +
+ +[![][back-to-top]](#readme-top) + +
+ ## 🛳 自主托管 如果您想自己部署此服务,可以按照以下步骤操作 diff --git a/package.json b/package.json index 56cd05c..0717c3c 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,15 @@ }, "license": "MIT", "author": "LobeHub ", + "type": "module", "scripts": { - "build": "node scripts/build.mjs", - "format": "node scripts/format.mjs", - "lint": "eslint \"scripts/**/*.mjs\" --fix", - "lint:md": "remark . --quiet --frail --output", - "prettier": "prettier -c --write \"**/*.{json,mjs,md}\"", - "test": "node scripts/test.mjs" + "awesome": "bun scripts/build.ts && bun scripts/updateAwesome.ts", + "build": "bun scripts/build.ts", + "format": "bun scripts/format.ts", + "lint": "eslint \"scripts/**/*.ts\" --fix", + "lint:md": "remark . --quiet --output", + "prettier": "prettier -c --write \"**/*.{json,ts,md}\"", + "test": "bun scripts/test.ts" }, "devDependencies": { "@lobehub/chat-plugin-sdk": "latest", diff --git a/scripts/build.mjs b/scripts/build.ts similarity index 78% rename from scripts/build.mjs rename to scripts/build.ts index 96e881d..c15630a 100644 --- a/scripts/build.mjs +++ b/scripts/build.ts @@ -2,9 +2,9 @@ import { consola } from 'consola'; import { cloneDeep, merge } from 'lodash-es'; import { resolve } from 'node:path'; -import { formatAndCheckSchema } from './check.mjs'; -import { config, localesDir, meta, plugins, pluginsDir, publicDir } from './const.mjs'; -import { checkDir, readJSON, writeJSON } from './utils.mjs'; +import { formatAndCheckSchema } from './check'; +import { config, localesDir, meta, plugins, pluginsDir, publicDir } from './const'; +import { checkDir, readJSON, writeJSON, findDuplicates } from './utils'; const build = async () => { checkDir(publicDir); @@ -32,7 +32,19 @@ const build = async () => { } for (const locale of [config.entryLocale, ...config.outputLocales]) { + // @ts-ignore pluginsIndex.plugins = list[locale].sort((a, b) => new Date(b.createAt) - new Date(a.createAt)); + + let tags: string[] = []; + + pluginsIndex.plugins.forEach((plugin) => { + tags = [...tags, ...plugin.meta.tags]; + }); + + tags = findDuplicates(tags); + + pluginsIndex.tags = tags + const name = locale === config.entryLocale ? `index.json` : `index.${locale}.json`; writeJSON(resolve(publicDir, name), pluginsIndex, false); consola.success(`build ${name}`); diff --git a/scripts/check.mjs b/scripts/check.ts similarity index 96% rename from scripts/check.mjs rename to scripts/check.ts index 4cf856f..5d808e5 100644 --- a/scripts/check.mjs +++ b/scripts/check.ts @@ -2,7 +2,7 @@ import { pluginMetaSchema } from '@lobehub/chat-plugin-sdk'; import { consola } from 'consola'; import dayjs from 'dayjs'; -import { meta } from './const.mjs'; +import { meta } from './const'; export const formatAndCheckSchema = (plugin) => { if (!plugin.schemaVersion) plugin.schemaVersion = meta.schemaVersion; diff --git a/scripts/const.mjs b/scripts/const.ts similarity index 70% rename from scripts/const.mjs rename to scripts/const.ts index 663f089..7d7297d 100644 --- a/scripts/const.mjs +++ b/scripts/const.ts @@ -2,18 +2,30 @@ import { readdirSync } from 'node:fs'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; -import { readJSON } from './utils.mjs'; +import { readJSON } from './utils'; export const __filename = fileURLToPath(import.meta.url); export const __dirname = dirname(__filename); export const root = resolve(__dirname, '..'); + export const pluginsDir = resolve(root, './plugins'); export const localesDir = resolve(root, './locales'); +export const publicDir = resolve(root, 'public'); + export const plugins = readdirSync(pluginsDir, { withFileTypes: true }); export const pluginLocales = readdirSync(localesDir, { withFileTypes: true }); + export const templatePath = resolve(root, 'plugin-template.json'); + +export const indexPath = resolve(publicDir, 'index.json'); +export const indexCnPath = resolve(publicDir, 'index.zh-CN.json'); + +export const readmePath = resolve(root, 'README.md'); +export const readmeCnPath = resolve(root, 'README.zh-CN.md'); + export const metaPath = resolve(root, 'meta.json'); export const meta = readJSON(metaPath); -export const publicDir = resolve(root, 'public'); + +export const readmeSplit = ''; export { default as config } from '../.i18nrc.js'; diff --git a/scripts/format.mjs b/scripts/format.ts similarity index 89% rename from scripts/format.mjs rename to scripts/format.ts index c2ca44e..c9483e5 100644 --- a/scripts/format.mjs +++ b/scripts/format.ts @@ -3,11 +3,11 @@ import { get, kebabCase, merge, set } from 'lodash-es'; import { existsSync } from 'node:fs'; import { resolve } from 'node:path'; -import { formatAndCheckSchema } from './check.mjs'; -import { config, localesDir, metaPath, plugins, pluginsDir, templatePath } from './const.mjs'; -import { formatFilenames } from './formatFilename.mjs'; -import { translateJSON } from './i18n.mjs'; -import { checkJSON, readJSON, split, writeJSON } from './utils.mjs'; +import { formatAndCheckSchema } from './check'; +import { config, localesDir, metaPath, plugins, pluginsDir, templatePath } from './const'; +import { formatFilenames } from './formatFilename'; +import { translateJSON } from './i18n'; +import { checkJSON, readJSON, split, writeJSON } from './utils'; const formatJSON = async (fileName, checkType) => { consola.start(fileName); diff --git a/scripts/formatFilename.mjs b/scripts/formatFilename.ts similarity index 96% rename from scripts/formatFilename.mjs rename to scripts/formatFilename.ts index d024a1e..820e1a2 100644 --- a/scripts/formatFilename.mjs +++ b/scripts/formatFilename.ts @@ -2,8 +2,8 @@ import { consola } from 'consola'; import { renameSync } from 'node:fs'; import { resolve } from 'node:path'; -import { localesDir, pluginLocales, plugins, pluginsDir } from './const.mjs'; -import { checkJSON, readJSON } from './utils.mjs'; +import { localesDir, pluginLocales, plugins, pluginsDir } from './const'; +import { checkJSON, readJSON } from './utils'; const formatFilenameById = (fileName) => { const filePath = resolve(pluginsDir, fileName); diff --git a/scripts/i18n.mjs b/scripts/i18n.ts similarity index 95% rename from scripts/i18n.mjs rename to scripts/i18n.ts index 3d16648..985857a 100644 --- a/scripts/i18n.mjs +++ b/scripts/i18n.ts @@ -3,7 +3,7 @@ import 'dotenv/config'; import { ChatOpenAI } from 'langchain/chat_models/openai'; import { HumanMessage, SystemMessage } from 'langchain/schema'; -import { config } from './const.mjs'; +import { config } from './const'; if (!process.env.OPENAI_TOKEN) { consola.error('cannot find OPENAI_TOKEN in env'); diff --git a/scripts/test.mjs b/scripts/test.ts similarity index 83% rename from scripts/test.mjs rename to scripts/test.ts index ca6209d..478f08a 100644 --- a/scripts/test.mjs +++ b/scripts/test.ts @@ -1,9 +1,9 @@ import { consola } from 'consola'; import { resolve } from 'node:path'; -import { checkUniqueIdentifier, formatAndCheckSchema } from './check.mjs'; -import { plugins, pluginsDir, root } from './const.mjs'; -import { readJSON } from './utils.mjs'; +import { checkUniqueIdentifier, formatAndCheckSchema } from './check'; +import { plugins, pluginsDir, root } from './const'; +import { readJSON } from './utils'; const runTest = () => { const identifiers = []; diff --git a/scripts/updateAwesome.ts b/scripts/updateAwesome.ts new file mode 100644 index 0000000..a4bbc53 --- /dev/null +++ b/scripts/updateAwesome.ts @@ -0,0 +1,51 @@ +import { readFileSync, writeFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +import { indexCnPath, indexPath, publicDir, readmeCnPath, readmePath } from './const'; +import { updateAwesomeReadme, readJSON } from './utils'; + +const updateAwesome = (filePath: string, md: string, plugins, locale?: string) => { + const data:string[] = []; + + plugins.forEach(({ identifier, author, createAt, homepage, meta }, i) => { + const pluginConfigPath = resolve( + publicDir, + [identifier, locale, 'json'].filter(Boolean).join('.'), + ); + const header = `### ${meta.title}`; + const subHeader = `By **[@${author}](${homepage})** on **${createAt}**`; + const desc = [ + `${meta.description}`, + `${meta.tags + .filter(Boolean) + .map((tag) => `\`${tag}\``) + .join(' ')}`, + ].join('\n\n'); + + const body:string = [ + i !== 0 ? '---' : false, + header, + subHeader, + desc, + `
\n\n[![][back-to-top]](#readme-top)\n\n
`, + ] + .filter(Boolean) + .join('\n\n'); + data.push(body); + }); + + const newMd = updateAwesomeReadme(md, data.join('\n\n')); + + writeFileSync(filePath, newMd, 'utf-8'); +}; + +const runUpdateAwesome = () => { + const readmeCn = readFileSync(readmeCnPath, 'utf-8'); + const readme = readFileSync(readmePath, 'utf-8'); + const index = readJSON(indexPath); + const indexCn = readJSON(indexCnPath); + updateAwesome(readmePath, readme, index.plugins); + updateAwesome(readmeCnPath, readmeCn, indexCn.plugins, 'zh-CN'); +}; + +runUpdateAwesome(); \ No newline at end of file diff --git a/scripts/utils.mjs b/scripts/utils.mjs deleted file mode 100644 index f4aaeaa..0000000 --- a/scripts/utils.mjs +++ /dev/null @@ -1,24 +0,0 @@ -import { consola } from 'consola'; -import { colors } from 'consola/utils'; -import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; - -export const readJSON = (filePath) => { - const data = readFileSync(filePath, 'utf8'); - return JSON.parse(data); -}; - -export const writeJSON = (filePath, data, format = true) => { - const jsonStr = format ? JSON.stringify(data, null, 2) : JSON.stringify(data); - writeFileSync(filePath, jsonStr, 'utf8'); -}; - -export const checkDir = (dirpath) => { - if (!existsSync(dirpath)) mkdirSync(dirpath); -}; - -export const checkJSON = (file) => file.isFile() && file.name?.includes('.json'); - -export const split = (name) => { - consola.log(''); - consola.log(colors.gray(`========================== ${name} ==============================`)); -}; diff --git a/scripts/utils.ts b/scripts/utils.ts new file mode 100644 index 0000000..8823ca2 --- /dev/null +++ b/scripts/utils.ts @@ -0,0 +1,56 @@ +import { consola } from 'consola'; +import { colors } from 'consola/utils'; +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; + +import { readmeSplit } from "./const" + +export const readJSON = (filePath) => { + const data = readFileSync(filePath, 'utf8'); + return JSON.parse(data); +}; + +export const writeJSON = (filePath, data, format = true) => { + const jsonStr = format ? JSON.stringify(data, null, 2) : JSON.stringify(data); + writeFileSync(filePath, jsonStr, 'utf8'); +}; + +export const checkDir = (dirpath) => { + if (!existsSync(dirpath)) mkdirSync(dirpath); +}; + +export const checkJSON = (file) => file.isFile() && file.name?.includes('.json'); + +export const split = (name) => { + consola.log(''); + consola.log(colors.gray(`========================== ${name} ==============================`)); +}; + +export const findDuplicates = (arr: string[]): string[] => { + const duplicates: { [key: string]: number } = {}; + + // 统计每个项目出现的次数 + for (const item of arr) { + if (duplicates[item]) { + duplicates[item]++; + } else { + duplicates[item] = 1; + } + } + + // 挑出重复出现 3 次以上的项目 + const COUNT = arr.length > 10 ? 3 : 1; + + const result = Object.keys(duplicates).filter((item) => duplicates[item] >= COUNT); + + // 按重复次数从多到少排序 + result.sort((a, b) => duplicates[b] - duplicates[a]); + + return result; +}; + +export const updateAwesomeReadme = (md: string, prompts: string): string => { + const mds = md.split(readmeSplit); + mds[1] = [' ', prompts, ' '].join('\n\n'); + + return mds.join(readmeSplit); +}; \ No newline at end of file