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