├── src ├── assets │ ├── base.scss │ ├── base.css │ └── base.css.map ├── core │ ├── commands │ │ ├── relax │ │ │ ├── ikun │ │ │ │ ├── ikun.mp4 │ │ │ │ ├── ikunCommand.ts │ │ │ │ ├── IkunBox.vue │ │ │ │ └── charVideo.js │ │ │ ├── todo │ │ │ │ ├── type.d.ts │ │ │ │ ├── subCommands │ │ │ │ │ └── addCommand.ts │ │ │ │ ├── todoCommand.ts │ │ │ │ └── TodoBox.vue │ │ │ └── moyu │ │ │ │ ├── moyuStore.ts │ │ │ │ ├── moyuCommand.ts │ │ │ │ └── MoYuBox.vue │ │ ├── user │ │ │ ├── userConstant.ts │ │ │ ├── type.d.ts │ │ │ ├── subCommands │ │ │ │ ├── logoutCommand.ts │ │ │ │ ├── loginCommand.ts │ │ │ │ └── registerCommand.ts │ │ │ ├── userApi.ts │ │ │ └── userCommands.ts │ │ ├── api │ │ │ ├── love │ │ │ │ ├── loveApi.ts │ │ │ │ └── loveCommand.ts │ │ │ ├── dujitang │ │ │ │ ├── dujitangApi.ts │ │ │ │ └── dujitangCommand.ts │ │ │ ├── inspire │ │ │ │ ├── inspireApi.ts │ │ │ │ └── inspireCommand.ts │ │ │ ├── music │ │ │ │ ├── musicApi.ts │ │ │ │ ├── musicCommand.ts │ │ │ │ └── MusicBox.vue │ │ │ ├── fanyi │ │ │ │ ├── fanYiApi.ts │ │ │ │ └── fanyiCommand.ts │ │ │ ├── varbook │ │ │ │ ├── varbookApi.ts │ │ │ │ ├── varbookCommand.ts │ │ │ │ └── VarbookBox.vue │ │ │ ├── film │ │ │ │ ├── filmCommand.ts │ │ │ │ └── filmApi.ts │ │ │ ├── hot │ │ │ │ ├── hotCommands.ts │ │ │ │ └── hotApi.ts │ │ │ ├── analyze │ │ │ │ ├── analyzeApi.ts │ │ │ │ └── analyzeCommand.ts │ │ │ └── background │ │ │ │ └── backgroundCommand.ts │ │ ├── terminal │ │ │ ├── shortcut │ │ │ │ ├── clearCommand.ts │ │ │ │ ├── ShortcutBox.vue │ │ │ │ └── shortcutCommand.ts │ │ │ ├── history │ │ │ │ └── historyCommand.ts │ │ │ ├── welcome │ │ │ │ └── welcomeCommand.ts │ │ │ ├── help │ │ │ │ ├── HelpBox.vue │ │ │ │ ├── helpCommand.ts │ │ │ │ ├── CommandHelpBox.vue │ │ │ │ └── helpUtils.ts │ │ │ ├── reset │ │ │ │ └── resetCommand.ts │ │ │ ├── hint │ │ │ │ └── hintCommand.ts │ │ │ └── theme │ │ │ │ └── themeCommand.ts │ │ ├── space │ │ │ ├── showCommand.ts │ │ │ ├── spaceCommands.ts │ │ │ ├── cdCommand.ts │ │ │ ├── mkdirCommand.ts │ │ │ ├── moveCommand.ts │ │ │ ├── listCommand.ts │ │ │ ├── copyCommand.ts │ │ │ ├── removeCommand.ts │ │ │ └── addCommand.ts │ │ ├── basic │ │ │ ├── dateCommand.ts │ │ │ ├── weather │ │ │ │ ├── weather.vue │ │ │ │ └── weatherCommand.ts │ │ │ ├── copyCommand.ts │ │ │ ├── gotoCommand.ts │ │ │ └── backColorCommand.ts │ │ ├── gpt │ │ │ ├── gptApi.ts │ │ │ ├── gptCommand.ts │ │ │ └── gptBox.vue │ │ ├── bot │ │ │ ├── botApi.ts │ │ │ ├── botCommand.ts │ │ │ └── botBox.vue │ │ └── search │ │ │ ├── mdnCommand.ts │ │ │ ├── bingCommand.ts │ │ │ ├── csdnCommand.ts │ │ │ ├── douyinCommand.ts │ │ │ ├── zhihuCommand.ts │ │ │ ├── doubanCommand.ts │ │ │ ├── juejinCommand.ts │ │ │ ├── githubCommand.ts │ │ │ ├── googleCommand.ts │ │ │ ├── searchCommands.ts │ │ │ ├── stackoverflowCommand.ts │ │ │ ├── youtubeCommand.ts │ │ │ ├── bilibiliCommand.ts │ │ │ └── baiduCommand.ts │ ├── command.d.ts │ ├── commandRegister.ts │ └── commandExecutor.ts ├── plugins │ ├── myDayjs.ts │ └── myAxios.ts ├── App.vue ├── router │ └── index.ts ├── stores │ ├── botStore.ts │ ├── gptStore.ts │ ├── userStore.ts │ ├── todoStore.ts │ ├── terminalConfigStore.ts │ └── spaceStore.ts ├── main.ts ├── utils │ ├── transferText.ts │ └── standard.ts ├── views │ ├── IndexPage.vue │ └── NotFound.vue └── components │ └── weirdo-terminal │ ├── history.ts │ ├── hint.ts │ ├── ContentOutput.vue │ ├── type.d.ts │ └── shortcuts.ts ├── env.d.ts ├── public └── favicon.ico ├── .vscode └── extensions.json ├── .prettierrc.json ├── tsconfig.json ├── server ├── src │ ├── thirdpart │ │ ├── filmApi.js │ │ ├── inspireApi.js │ │ ├── loveApi.js │ │ ├── dujitangApi.js │ │ ├── backgroundApi.js │ │ ├── analyzeApi.js │ │ ├── hotApi.js │ │ ├── varbookApi.js │ │ ├── musicApi.js │ │ ├── baiduFanYiApi.js │ │ ├── gptApi.js │ │ └── botApi.js │ ├── exception │ │ ├── index.js │ │ └── errorCode.js │ ├── config │ │ └── getConfig.js │ ├── service │ │ ├── musicService.js │ │ └── userService.js │ ├── index.js │ ├── controller │ │ ├── filmController.js │ │ ├── loveController.js │ │ ├── inspireController.js │ │ ├── botController.js │ │ ├── dujitangController.js │ │ ├── varbookController.js │ │ ├── backgroundController.js │ │ ├── gptController.js │ │ ├── hotController.js │ │ ├── analyzeController.js │ │ ├── fanyiController.js │ │ ├── musicController.js │ │ └── userController.js │ ├── db │ │ ├── db.sql │ │ └── db.js │ ├── model │ │ └── user.js │ ├── routes │ │ └── routes.js │ └── server │ │ └── server.js ├── Dockerfile ├── package.json ├── .gitignore └── .dockerignore ├── testgpt ├── package.json └── index.js ├── .gitignore ├── Dockerfile ├── tsconfig.node.json ├── index.html ├── tsconfig.app.json ├── postcss.config.js ├── .eslintrc.cjs ├── LICENSE ├── vite.config.ts ├── components.d.ts ├── testBot └── index.js ├── package.json └── README.md /src/assets/base.scss: -------------------------------------------------------------------------------- 1 | $bgColor: #ccc; -------------------------------------------------------------------------------- /src/assets/base.css: -------------------------------------------------------------------------------- 1 | /*# sourceMappingURL=base.css.map */ -------------------------------------------------------------------------------- /env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module 'amfe-flexible' -------------------------------------------------------------------------------- /src/assets/base.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"base.css"} -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2WeirDo/weirdo_terminal/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/core/commands/relax/ikun/ikun.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2WeirDo/weirdo_terminal/HEAD/src/core/commands/relax/ikun/ikun.mp4 -------------------------------------------------------------------------------- /src/core/commands/user/userConstant.ts: -------------------------------------------------------------------------------- 1 | import UserType = User.UserType 2 | 3 | /** 4 | * 本地用户 5 | */ 6 | export const LOCAL_USER: UserType = { 7 | username: 'local' 8 | } 9 | -------------------------------------------------------------------------------- /src/core/commands/api/love/loveApi.ts: -------------------------------------------------------------------------------- 1 | import myAxios from '@/plugins/myAxios' 2 | 3 | export const getLove = async () => { 4 | return await myAxios.post('/love/get/random') 5 | } 6 | -------------------------------------------------------------------------------- /src/plugins/myDayjs.ts: -------------------------------------------------------------------------------- 1 | import dayjs from "dayjs"; 2 | import Duration from 'dayjs/plugin/duration' 3 | 4 | dayjs.locale("zh-cn"); 5 | 6 | dayjs.extend(Duration); 7 | 8 | export default dayjs; -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "Vue.volar", 4 | "Vue.vscode-typescript-vue-plugin", 5 | "dbaeumer.vscode-eslint", 6 | "esbenp.prettier-vscode" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/core/commands/api/dujitang/dujitangApi.ts: -------------------------------------------------------------------------------- 1 | import myAxios from '@/plugins/myAxios' 2 | 3 | export const getDujitang = async () => { 4 | return await myAxios.post('/dujitang/get/random') 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "semi": false, 4 | "tabWidth": 2, 5 | "singleQuote": true, 6 | "printWidth": 100, 7 | "trailingComma": "none" 8 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { 5 | "path": "./tsconfig.node.json" 6 | }, 7 | { 8 | "path": "./tsconfig.app.json" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/core/commands/user/type.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace User { 2 | /** 3 | * 用户类型 4 | */ 5 | interface UserType { 6 | username: string 7 | email?: string 8 | createTime?: date 9 | updateTime?: date 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 14 | -------------------------------------------------------------------------------- /src/core/commands/relax/todo/type.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Todo { 2 | /** 3 | * 任务类型 4 | */ 5 | interface TaskType { 6 | name: string 7 | isFinished: boolean 8 | createTime: date 9 | finishTime?: date 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /server/src/thirdpart/filmApi.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | async function getFilm() { 4 | const api = "https://api.vvhan.com/api/douban"; 5 | return await axios.get(api).then((res) => res.data.data); 6 | } 7 | export { 8 | getFilm, 9 | }; 10 | -------------------------------------------------------------------------------- /server/src/exception/index.js: -------------------------------------------------------------------------------- 1 | class MyError extends Error { 2 | constructor(code, message) { 3 | super(message); 4 | this.code = code; 5 | this.message = message; 6 | this.name = "MyError"; 7 | } 8 | } 9 | 10 | export default MyError; 11 | -------------------------------------------------------------------------------- /src/core/commands/api/inspire/inspireApi.ts: -------------------------------------------------------------------------------- 1 | import myAxios from '@/plugins/myAxios' 2 | 3 | export const getInspire = async () => { 4 | let res:any = await myAxios.post('/inspire/get/random'); 5 | res = `${res.data.en}   ${res.data.zh}` 6 | return res; 7 | } 8 | -------------------------------------------------------------------------------- /server/src/config/getConfig.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取当前环境的配置 3 | * @author weirdo 4 | */ 5 | let config 6 | const env = process.env.NODE_ENV ?? 'local' 7 | 8 | if (env === 'local') { 9 | config = import('./config.js') 10 | } else { 11 | config = import(`./config.${env}.js`) 12 | } 13 | 14 | export default config 15 | 16 | -------------------------------------------------------------------------------- /server/src/thirdpart/inspireApi.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | async function getInspire() { 4 | const api = "https://api.vvhan.com/api/en?type=sj"; 5 | let q = await axios.get(api).then((res) => res.data.data); 6 | console.log("q: ", q); 7 | return q; 8 | } 9 | 10 | export { 11 | getInspire, 12 | }; 13 | -------------------------------------------------------------------------------- /server/src/thirdpart/loveApi.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | /** 4 | * 随机获取情话 5 | * @return {Promise<*[]>} 6 | */ 7 | async function getLove() { 8 | const api = "https://api.vvhan.com/api/love?type=json"; 9 | return await axios.get(api).then((res) => res.data.ishan); 10 | } 11 | 12 | export { 13 | getLove, 14 | }; 15 | -------------------------------------------------------------------------------- /src/core/commands/api/music/musicApi.ts: -------------------------------------------------------------------------------- 1 | import myAxios from '../../../../plugins/myAxios' 2 | 3 | /** 4 | * 搜索单条音乐 5 | * @param keywords 6 | */ 7 | export const getSingleMusic = async (keywords: string) => { 8 | if (!keywords) { 9 | return null 10 | } 11 | return await myAxios.post('/music/get', { keywords }) 12 | } 13 | -------------------------------------------------------------------------------- /server/src/service/musicService.js: -------------------------------------------------------------------------------- 1 | // 业务逻辑层 2 | import { searchMusics } from '../thirdpart/musicApi.js' 3 | 4 | async function getSingleMusic(keywords) { 5 | const songs = await searchMusics(keywords, 1) 6 | if (songs.length < 1) { 7 | return null 8 | } 9 | return songs[0] 10 | } 11 | 12 | export { 13 | getSingleMusic 14 | } 15 | -------------------------------------------------------------------------------- /server/src/thirdpart/dujitangApi.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | /** 4 | * 随机获取背景 5 | * @return {Promise<*[]>} 6 | */ 7 | async function getDujitang() { 8 | const api = "https://api.btstu.cn/yan/api.php?charset=utf-8&encode=json"; 9 | return await axios.get(api).then((res) => res.data.text); 10 | } 11 | 12 | export { 13 | getDujitang, 14 | }; 15 | -------------------------------------------------------------------------------- /server/src/thirdpart/backgroundApi.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | 3 | /** 4 | * 随机获取背景 5 | * @return {Promise<*[]>} 6 | */ 7 | async function getRandomBackground() { 8 | const api = 'https://api.btstu.cn/sjbz/api.php?lx=dongman&format=json' 9 | return await axios.get(api).then((res) => res.data.imgurl) 10 | } 11 | 12 | export { 13 | getRandomBackground 14 | } 15 | -------------------------------------------------------------------------------- /server/src/thirdpart/analyzeApi.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | /** 4 | * 获取网站技术信息 5 | * @return {Promise<*[]>} 6 | */ 7 | 8 | async function getAnalyze(link) { 9 | if (!link) return null 10 | const api = `https://api.asilu.com/php/web-info.php?url=${link}` 11 | return await axios.get(api).then(res => res.data); 12 | } 13 | 14 | export { 15 | getAnalyze 16 | } 17 | -------------------------------------------------------------------------------- /src/core/commands/api/fanyi/fanYiApi.ts: -------------------------------------------------------------------------------- 1 | import myAxios from '@/plugins/myAxios' 2 | 3 | /** 4 | * 翻译文本 5 | * @param keywords 6 | * @param config 7 | */ 8 | export const translate = async (keywords: string, config: Record) => { 9 | if (!keywords) { 10 | return null 11 | } 12 | return await myAxios.post('/fanyi/translate', { keywords, config }) 13 | } 14 | -------------------------------------------------------------------------------- /server/src/thirdpart/hotApi.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | /** 4 | * 随机获取热榜信息 5 | * @return {Promise<*[]>} 6 | */ 7 | 8 | async function getHot(platform) { 9 | if (!platform) { 10 | return null 11 | } 12 | const api = `https://api.vvhan.com/api/hotlist?type=${platform}` 13 | return await axios.get(api).then((res) => res.data.data) 14 | } 15 | 16 | export { 17 | getHot 18 | } 19 | -------------------------------------------------------------------------------- /testgpt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "testgpt", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "description": "", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "axios": "^1.4.0", 15 | "chatgpt": "^5.2.5", 16 | "openai": "^3.2.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /server/src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 创建一个基于云托管(CloudBase Run)的服务器实例, 3 | * 并注册一些接口路由,最后监听指定的端口以便接受请求。 4 | */ 5 | 6 | import { CloudBaseRunServer } from './server/server.js' 7 | import routes from './routes/routes.js' 8 | 9 | // 创建云托管 Server 实例 10 | const server = new CloudBaseRunServer() 11 | 12 | // 注册接口路由 13 | for (const route of routes) { 14 | server.setRoute(route.path, route.handler) 15 | } 16 | 17 | // 监听端口 18 | server.listen(7345) 19 | -------------------------------------------------------------------------------- /server/Dockerfile: -------------------------------------------------------------------------------- 1 | # 使用官方 Node.js 轻量级镜像 2 | # https://hub.docker.com/_/node 3 | FROM node:16-slim 4 | 5 | # 定义工作目录, 即/usr/src/app。所有后续命令将在这个目录下执行。 6 | WORKDIR /usr/src/app 7 | 8 | # 将本地代码复制到工作目录内 9 | COPY ./ ./ 10 | 11 | RUN npm install 12 | 13 | # 安装 pm2 14 | RUN npm install pm2 -g 15 | 16 | # 启动服务 17 | # 使用pm2-runtime命令来启动你的Node.js应用程序,而不是直接运行npm start。 18 | # 使用pm2-runtime可以更好地管理Node.js应用程序的生命周期,包括进程监视和自动重启。 19 | CMD pm2-runtime 'npm start' 20 | -------------------------------------------------------------------------------- /src/core/commands/api/inspire/inspireCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { getInspire } from './inspireApi' 3 | 4 | 5 | const inspireCommand: CommandType = { 6 | func: 'inspire', 7 | name: '励志句子', 8 | options: [], 9 | async action(options, terminal) { 10 | const res = await getInspire(); 11 | terminal.writeTextSuccessResult(`每日一句:
${res}`) 12 | } 13 | } 14 | 15 | export default inspireCommand 16 | -------------------------------------------------------------------------------- /server/src/controller/filmController.js: -------------------------------------------------------------------------------- 1 | import { getFilm } from '../thirdpart/filmApi.js' 2 | import MyError from '../exception/index.js' 3 | import { THIRD_PART_SERVICE_ERROR_CODE } from '../exception/errorCode.js' 4 | 5 | async function getFilmApi(event, req, res) { 6 | const result = await getFilm() 7 | if (!result) { 8 | throw new MyError(THIRD_PART_SERVICE_ERROR_CODE) 9 | } 10 | return result 11 | } 12 | 13 | export { 14 | getFilmApi 15 | } 16 | -------------------------------------------------------------------------------- /src/core/commands/api/varbook/varbookApi.ts: -------------------------------------------------------------------------------- 1 | import myAxios from '@/plugins/myAxios' 2 | import { enBase64 } from '@/utils/standard' 3 | 4 | export const getNamedVariables = async (searchText: string) => { 5 | const s = enBase64(searchText) 6 | const params = {s} 7 | const res = await myAxios.post('/varbook/get', { params }) 8 | // console.log("res ", res.data.data); 9 | // return res.data.data 10 | console.log("res ", res); 11 | return res; 12 | } 13 | -------------------------------------------------------------------------------- /src/core/commands/terminal/shortcut/clearCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | 3 | /** 4 | * 清屏命令 5 | * @author weirdo 6 | */ 7 | const clearCommand: CommandType = { 8 | func: 'clear', 9 | name: '清屏', 10 | alias: ['cl'], 11 | options: [], 12 | action(options, terminal): void { 13 | // 延时,把当前这条 clear 命令也清掉 14 | setTimeout(() => { 15 | terminal.clear() 16 | }, 100) 17 | } 18 | } 19 | 20 | export default clearCommand 21 | -------------------------------------------------------------------------------- /src/core/commands/api/love/loveCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { getLove } from './loveApi' 3 | 4 | /** 5 | * 来情话看看 6 | * @author weirdo 7 | */ 8 | const loveCommand: CommandType = { 9 | func: 'love', 10 | name: '情话', 11 | options: [], 12 | async action(options, terminal) { 13 | // 随机获取情话 14 | const res = await getLove() 15 | terminal.writeTextSuccessResult(res.data) 16 | } 17 | } 18 | 19 | export default loveCommand 20 | -------------------------------------------------------------------------------- /src/core/commands/api/dujitang/dujitangCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { getDujitang } from './dujitangApi' 3 | 4 | /** 5 | * 来毒鸡汤看看 6 | * @author weirdo 7 | */ 8 | const dujitangCommand: CommandType = { 9 | func: 'dujitang', 10 | name: '毒鸡汤', 11 | options: [], 12 | async action(options, terminal) { 13 | // 随机获取毒鸡汤 14 | const res = await getDujitang(); 15 | terminal.writeTextSuccessResult(res.data) 16 | } 17 | } 18 | 19 | export default dujitangCommand 20 | -------------------------------------------------------------------------------- /src/core/commands/api/film/filmCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { getFilm } from './filmApi' 3 | 4 | /** 5 | * @author weirdo 6 | */ 7 | const filmCommand: CommandType = { 8 | func: 'film', 9 | name: '电影热门信息', 10 | alias: ['dianying'], 11 | params: [], 12 | options: [], 13 | async action(options, terminal) { 14 | const res: any = await getFilm() 15 | terminal.writeTextSuccessResult(`成功请求热门电影信息
${res}`) 16 | } 17 | } 18 | 19 | export default filmCommand 20 | -------------------------------------------------------------------------------- /src/core/commands/space/showCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from "../../command"; 2 | import { useSpaceStore } from "@/stores/spaceStore"; 3 | 4 | /** 5 | * 显示当前所在目录 6 | */ 7 | const showCommand: CommandType = { 8 | func: "show", 9 | name: "显示当前空间位置", 10 | options: [], 11 | action(options, terminal): void { 12 | const spaceStore = useSpaceStore(); 13 | const output = `当前目录:${spaceStore.currentDir}`; 14 | terminal.writeTextResult(output); 15 | }, 16 | }; 17 | 18 | export default showCommand; 19 | -------------------------------------------------------------------------------- /src/core/commands/api/film/filmApi.ts: -------------------------------------------------------------------------------- 1 | import myAxios from '@/plugins/myAxios' 2 | 3 | export const getFilm = async () => { 4 | let res: any = await myAxios.post('/film/get') 5 | if (res.code === 0) { 6 | res = res.data 7 | res = res.map((item: any) => { 8 | return ( 9 | `⭐: ${item?.info?.pingfen}` + 10 | `  ${item?.title}
` 11 | ) 12 | }) 13 | return res 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/core/commands/basic/dateCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../command' 2 | import myDayjs from '../../../plugins/myDayjs' 3 | 4 | /** 5 | * 日期命令 6 | * @author weirdo 7 | */ 8 | const dateCommand: CommandType = { 9 | func: 'date', 10 | name: '日期', 11 | options: [], 12 | action(options, terminal): void { 13 | const dateStr = myDayjs().format('YYYY-MM-DD HH:mm:ss') 14 | const output = `当前时间:${dateStr}` 15 | terminal.writeTextResult(output) 16 | } 17 | } 18 | 19 | export default dateCommand 20 | -------------------------------------------------------------------------------- /src/core/commands/relax/moyu/moyuStore.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | export const useMoyuStore = defineStore('moyuConfig', { 4 | state: () => ({ 5 | preGame: 'https://haiyong.site/moyu/wzq-2/', 6 | }), 7 | getters: {}, 8 | // 持久化 9 | persist: { 10 | key: 'moyu-config-store', // Key 用于引用 storage 中的数据 11 | storage: window.localStorage, // 将数据持久化到的 storage 中 12 | }, 13 | actions: { 14 | setPreGame(url: string) { 15 | if (!url) return 16 | this.preGame = url 17 | }, 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /src/core/commands/gpt/gptApi.ts: -------------------------------------------------------------------------------- 1 | import myAxios from '@/plugins/myAxios' 2 | import { useGptStore } from '@/stores/gptStore' 3 | /** 4 | * gpt请求 5 | * @param message 消息 6 | * @param memory 历史消息记录 7 | */ 8 | export const getGptOutput = async (message: string, memory: any, need: boolean) => { 9 | if (!message) return null 10 | let res: any = await myAxios.post('/gpt/get', { message, memory, need }) 11 | res = res.data 12 | // let [text, id] = res 13 | let gptStore = useGptStore() 14 | gptStore.addRecord(message) 15 | return res 16 | } 17 | -------------------------------------------------------------------------------- /src/core/commands/basic/weather/weather.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 24 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | import { type RouteRecordRaw } from 'vue-router' 3 | import IndexPage from '../views/IndexPage.vue' 4 | // import NotFound from '../views/NotFound.vue' 5 | 6 | const routes: RouteRecordRaw[] = [ 7 | { path: '/', component: IndexPage }, 8 | // { 9 | // path: '/:pathMatch(.*)*', 10 | // component: NotFound 11 | // } 12 | ] 13 | 14 | const router = createRouter({ 15 | history: createWebHistory(import.meta.env.BASE_URL), 16 | routes 17 | }) 18 | 19 | export default router 20 | -------------------------------------------------------------------------------- /src/stores/botStore.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | /** 4 | * gpt 5 | */ 6 | 7 | export const useBotStore = defineStore('bot', { 8 | state: (): any => ({ 9 | memory: [] 10 | }), 11 | getters: {}, 12 | persist: { 13 | key: 'bot-store', 14 | storage: window.localStorage, 15 | beforeRestore: (context) => {}, 16 | afterRestore: (context) => {} 17 | }, 18 | actions: { 19 | addRecord(item: any) { 20 | let { memory } = this.$state 21 | if (memory.length >= 30) this.$reset() 22 | memory.push(item) 23 | } 24 | } 25 | }) 26 | -------------------------------------------------------------------------------- /testgpt/index.js: -------------------------------------------------------------------------------- 1 | import { ChatGPTAPI } from 'chatgpt' 2 | 3 | async function example() { 4 | const api = new ChatGPTAPI({ 5 | apiBaseUrl: '', 6 | apiKey: '' 7 | }) 8 | let res = await api.sendMessage('你是什么') 9 | console.log(res.text) 10 | 11 | // send a follow-up 12 | res = await api.sendMessage('你能扩充一下吗', { 13 | parentMessageId: res.id 14 | }) 15 | console.log(res.text) 16 | 17 | // send another follow-up 18 | res = await api.sendMessage('我们刚刚谈论了什么?', { 19 | parentMessageId: res.id 20 | }) 21 | console.log(res.text) 22 | } 23 | 24 | example() 25 | 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 用于构建一个基于 Node.js 的前端应用程序的 Docker 镜像。 2 | 3 | # 用于指定基础镜像。它基于名为"node"的官方Docker镜像, 4 | # 并使用了18版本的Node.js,以及一个基于Debian Buster的slim操作系统版本。 5 | FROM node:18-buster-slim 6 | 7 | RUN npm install -g http-server 8 | 9 | # 将Docker容器内的工作目录切换到/src。之后的所有命令将在这个目录下执行。 10 | WORKDIR /src 11 | 12 | # 将当前Dockerfile所在目录中的所有文件和文件夹复制到容器的/src目录下。 13 | # 这个步骤将你的前端应用程序代码复制到了容器内,以便进行后续操作。 14 | COPY . . 15 | 16 | RUN npm install 17 | 18 | RUN npm run build 19 | 20 | # 指定容器将要监听的端口号 21 | EXPOSE 80 22 | 23 | # 定义了容器启动时要执行的命令 24 | # 容器将会使用http-server来启动一个HTTP服务器,从名为"dist"的目录中提供静态文件 25 | CMD [ "http-server", "dist" ] 26 | 27 | -------------------------------------------------------------------------------- /server/src/controller/loveController.js: -------------------------------------------------------------------------------- 1 | // 请求层 : 接收前端发起的请求, 拿到参数 2 | // 请求层调用逻辑层的方法或者调用第三方api 3 | import { getLove } from '../thirdpart/loveApi.js' 4 | import MyError from '../exception/index.js' 5 | import { THIRD_PART_SERVICE_ERROR_CODE } from '../exception/errorCode.js' 6 | 7 | /** 8 | * 随机获取情话 9 | * @param event 10 | * @param req 11 | * @param res 12 | */ 13 | async function getRandomLoveApi(event, req, res) { 14 | // 调用封装的第三方api 15 | const result = await getLove() 16 | if (!result) { 17 | throw new MyError(THIRD_PART_SERVICE_ERROR_CODE) 18 | } 19 | return result 20 | } 21 | 22 | export { 23 | getRandomLoveApi 24 | } 25 | -------------------------------------------------------------------------------- /src/core/commands/terminal/shortcut/ShortcutBox.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node18/tsconfig.json", 3 | "include": [ 4 | "vite.config.*", 5 | "vitest.config.*", 6 | "cypress.config.*", 7 | "nightwatch.conf.*", 8 | "playwright.config.*" 9 | ], 10 | "compilerOptions": { 11 | "composite": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Bundler", 14 | "types": ["node"], 15 | "removeComments": true, // 是否移除 TypeScript 代码中的注释 16 | "noEmitHelpers": true, // 是否禁止生成 TypeScript 帮助函数 17 | "downlevelIteration": true, // 是否将 for...of 循环编译为适用于旧版 JavaScript 的代码 18 | "importHelpers": true // 是否使用模块化方式引入帮助函数 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/src/exception/errorCode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 错误码 3 | * @type {number} 4 | */ 5 | 6 | // 请求参数错误 7 | const REQUEST_PARAMS_ERROR_CODE = 40000; 8 | 9 | // 无权限访问 10 | const NO_AUTH_ERROR_CODE = 40100; 11 | 12 | // 访问被禁止 13 | const FORBIDDEN_ERROR_CODE = 40300; 14 | 15 | // 系统错误 16 | const SYSTEM_ERROR_CODE = 50000; 17 | 18 | // 找不到数据 19 | const NOT_FOUND_ERROR_CODE = 40400; 20 | 21 | // 第三方服务错误 22 | const THIRD_PART_SERVICE_ERROR_CODE = 50010; 23 | 24 | export { 25 | REQUEST_PARAMS_ERROR_CODE, 26 | NO_AUTH_ERROR_CODE, 27 | FORBIDDEN_ERROR_CODE, 28 | SYSTEM_ERROR_CODE, 29 | NOT_FOUND_ERROR_CODE, 30 | THIRD_PART_SERVICE_ERROR_CODE, 31 | }; 32 | -------------------------------------------------------------------------------- /src/core/commands/basic/copyCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../command' 2 | 3 | /** 4 | * nihao 命令(整活) 5 | * @author weirdo 6 | */ 7 | const nihaoCommand: CommandType = { 8 | func: 'copy', 9 | name: '复制文本', 10 | params: [ 11 | { 12 | key: 'text', 13 | desc: '需复制文本', 14 | required: true 15 | } 16 | ], 17 | options: [], 18 | collapsible: true, 19 | action(options, terminal) { 20 | // console.log(options); 21 | const text = options._.join(' ') 22 | navigator.clipboard.writeText(text) 23 | terminal.writeTextSuccessResult('文本已复制到剪贴板') 24 | } 25 | } 26 | 27 | export default nihaoCommand 28 | -------------------------------------------------------------------------------- /server/src/controller/inspireController.js: -------------------------------------------------------------------------------- 1 | // 请求层 : 接收前端发起的请求, 拿到参数 2 | // 请求层调用逻辑层的方法或者调用第三方api 3 | import { getInspire } from '../thirdpart/inspireApi.js' 4 | import MyError from '../exception/index.js' 5 | import { THIRD_PART_SERVICE_ERROR_CODE } from '../exception/errorCode.js' 6 | 7 | /** 8 | * 随机获取励志句子 9 | * @param event 10 | * @param req 11 | * @param res 12 | */ 13 | async function getRandomInspireApi(event, req, res) { 14 | // 调用封装的第三方api 15 | const result = await getInspire() 16 | if (!result) { 17 | throw new MyError(THIRD_PART_SERVICE_ERROR_CODE) 18 | } 19 | return result 20 | } 21 | 22 | export { 23 | getRandomInspireApi 24 | } 25 | -------------------------------------------------------------------------------- /server/src/db/db.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `user` 2 | ( 3 | `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', 4 | `username` varchar(256) NOT NULL COMMENT '用户名', 5 | `password` varchar(512) NOT NULL COMMENT '密码', 6 | `email` varchar(512) DEFAULT NULL COMMENT '邮箱', 7 | `status` int(11) NOT NULL DEFAULT '0' COMMENT '状态 0 - 正常', 8 | `createTime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 9 | `updateTime` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 10 | `isDelete` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除', 11 | PRIMARY KEY (`id`) 12 | ) ENGINE = InnoDB COMMENT ='用户' -------------------------------------------------------------------------------- /src/core/commands/terminal/history/historyCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | 3 | /** 4 | * 查看历史命令 5 | * @author weirdo 6 | */ 7 | const historyCommand: CommandType = { 8 | func: 'history', 9 | name: '查看执行历史', 10 | alias: ['h'], 11 | options: [], 12 | collapsible: true, 13 | action(options, terminal): void { 14 | const commandOutputTypes = terminal.listCommandHistory() 15 | terminal.writeTextResult(`⭐️ 输入 ![序号] 可以快速执行某条历史命令`) 16 | commandOutputTypes.forEach((command: { text: any }, index: number) => { 17 | terminal.writeTextResult(`${index + 1} ${command.text}`) 18 | }) 19 | } 20 | } 21 | 22 | export default historyCommand 23 | -------------------------------------------------------------------------------- /server/src/controller/botController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bot接口 3 | * @param event 4 | * @param req 5 | * @param res 6 | */ 7 | import { ask } from '../thirdpart/botApi.js' 8 | import MyError from '../exception/index.js' 9 | import { REQUEST_PARAMS_ERROR_CODE, THIRD_PART_SERVICE_ERROR_CODE } from '../exception/errorCode.js' 10 | async function getBotApi(event, req, res) { 11 | const { message, memory } = event 12 | if (!message) { 13 | throw new MyError(REQUEST_PARAMS_ERROR_CODE, '请输入消息') 14 | } 15 | const result = await ask(memory, message) 16 | if (!result) { 17 | throw new MyError(THIRD_PART_SERVICE_ERROR_CODE) 18 | } 19 | return result 20 | } 21 | 22 | export { getBotApi } 23 | -------------------------------------------------------------------------------- /src/core/commands/relax/ikun/ikunCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { defineAsyncComponent } from 'vue' 3 | import ComponentOutputType = WeirdoTerminal.ComponentOutputType 4 | 5 | /** 6 | * ikun 命令(整活) 7 | * @author weirdo 8 | */ 9 | const ikunCommand: CommandType = { 10 | func: 'ikun', 11 | name: '测试你是不是小黑子🐔', 12 | options: [], 13 | collapsible: true, 14 | action(options, terminal) { 15 | const output: ComponentOutputType = { 16 | type: 'component', 17 | component: defineAsyncComponent(() => import('./IkunBox.vue')), 18 | props: {} 19 | } 20 | terminal.writeResult(output) 21 | } 22 | } 23 | 24 | export default ikunCommand 25 | -------------------------------------------------------------------------------- /server/src/controller/dujitangController.js: -------------------------------------------------------------------------------- 1 | // 请求层 : 接收前端发起的请求, 拿到参数 2 | // 请求层调用逻辑层的方法或者调用第三方api 3 | import { getDujitang } from '../thirdpart/dujitangApi.js' 4 | import MyError from '../exception/index.js' 5 | import { THIRD_PART_SERVICE_ERROR_CODE } from '../exception/errorCode.js' 6 | 7 | /** 8 | * 随机获取背景 9 | * @param event 10 | * @param req 11 | * @param res 12 | */ 13 | // routes中调用getRandomBackgroundApi 14 | async function getRandomDujitangApi(event, req, res) { 15 | // 调用封装的第三方api 16 | const result = await getDujitang() 17 | if (!result) { 18 | throw new MyError(THIRD_PART_SERVICE_ERROR_CODE) 19 | } 20 | return result 21 | } 22 | 23 | export { 24 | getRandomDujitangApi 25 | } 26 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { createPinia } from 'pinia' 3 | import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' 4 | import App from './App.vue' 5 | import router from './router' 6 | // import 'amfe-flexible' 7 | 8 | const app = createApp(App) 9 | app.config.warnHandler = (msg, vm, trace) => { 10 | // 如果警告信息包含特定内容,你可以在此处判断并忽略该警告 11 | if (msg.includes('Vue received a Component which was made a reactive object')) { 12 | return 13 | } 14 | // 对于其他警告,可以按照需要进行处理 15 | console.warn(msg, vm, trace) 16 | } 17 | 18 | const pinia = createPinia() 19 | pinia.use(piniaPluginPersistedstate) 20 | 21 | app.use(pinia) 22 | app.use(router) 23 | 24 | app.mount('#app') 25 | -------------------------------------------------------------------------------- /server/src/controller/varbookController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * gpt接口 3 | * @param event 4 | * @param req 5 | * @param res 6 | */ 7 | import { getVarbookOutput } from '../thirdpart/varbookApi.js' 8 | import MyError from '../exception/index.js' 9 | import { REQUEST_PARAMS_ERROR_CODE, THIRD_PART_SERVICE_ERROR_CODE } from '../exception/errorCode.js' 10 | async function getVarbookApi(event, req, res) { 11 | const { params } = event 12 | if (!params) { 13 | throw new MyError(REQUEST_PARAMS_ERROR_CODE, '请输入变量描述') 14 | } 15 | const result = await getVarbookOutput(params) 16 | if (!result) { 17 | throw new MyError(THIRD_PART_SERVICE_ERROR_CODE) 18 | } 19 | return result 20 | } 21 | 22 | export { getVarbookApi } 23 | -------------------------------------------------------------------------------- /src/core/commands/basic/weather/weatherCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { defineAsyncComponent } from 'vue' 3 | import ComponentOutputType = WeirdoTerminal.ComponentOutputType 4 | 5 | /** 6 | * 查看现在天气 7 | * @author weirdo 8 | */ 9 | const ddosCommand: CommandType = { 10 | func: 'weather', 11 | name: '查看天气', 12 | alias: ['nw'], 13 | collapsible: true, 14 | options: [], 15 | action(options, terminal) { 16 | const output: ComponentOutputType = { 17 | type: 'component', 18 | component: defineAsyncComponent(() => import('./weather.vue')), 19 | props: {} 20 | } 21 | terminal.writeResult(output) 22 | } 23 | } 24 | 25 | export default ddosCommand 26 | -------------------------------------------------------------------------------- /server/src/controller/backgroundController.js: -------------------------------------------------------------------------------- 1 | // 请求层 : 接收前端发起的请求, 拿到参数 2 | // 请求层调用逻辑层的方法或者调用第三方api 3 | import { getRandomBackground } from '../thirdpart/backgroundApi.js' 4 | import MyError from '../exception/index.js' 5 | import { THIRD_PART_SERVICE_ERROR_CODE } from '../exception/errorCode.js' 6 | 7 | /** 8 | * 随机获取背景 9 | * @param event 10 | * @param req 11 | * @param res 12 | */ 13 | // routes中调用getRandomBackgroundApi 14 | async function getRandomBackgroundApi(event, req, res) { 15 | // 调用封装的第三方api 16 | const result = await getRandomBackground() 17 | if (!result) { 18 | throw new MyError(THIRD_PART_SERVICE_ERROR_CODE) 19 | } 20 | return result 21 | } 22 | 23 | export { 24 | getRandomBackgroundApi 25 | } 26 | -------------------------------------------------------------------------------- /src/core/commands/bot/botApi.ts: -------------------------------------------------------------------------------- 1 | import myAxios from '@/plugins/myAxios' 2 | import { useBotStore } from '@/stores/botStore' 3 | import { storeToRefs } from 'pinia' 4 | 5 | /** 6 | * gpt请求 7 | * @param message 消息 8 | * @param memory 历史消息记录 9 | */ 10 | export const getBotOutput = async (message: string) => { 11 | if (!message) return null 12 | let botStore = useBotStore() 13 | // bug 14 | // botStore.addRecord({ role: 'user', content: message }) 15 | let { memory } = botStore 16 | let res: any = await myAxios.post('/bot/get', { message, memory }) 17 | res = res.data 18 | botStore.addRecord({ role: 'user', content: message }) 19 | botStore.addRecord({ role: 'assistant', content: res }) 20 | return res 21 | } 22 | -------------------------------------------------------------------------------- /src/stores/gptStore.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | /** 4 | * gpt 5 | */ 6 | 7 | export const useGptStore = defineStore('gpt', { 8 | state: (): any => ({ 9 | // bug 10 | // ❗这里一定一定不能用record作为数组, 因为好像跟pinia封装的有关, 因为这个搞了很久 11 | memory: [] 12 | }), 13 | getters: {}, 14 | persist: { 15 | key: 'gpt-store', 16 | storage: window.localStorage, 17 | beforeRestore: (context) => {}, 18 | afterRestore: (context) => {} 19 | }, 20 | actions: { 21 | addRecord(item: string) { 22 | let { memory } = this.$state 23 | // bug 不能直接设置数组为[], 最好使用this.$reset()重置数据 24 | if (memory.length >= 5) this.$reset() 25 | memory.push(item) // 尝试更新记录 26 | } 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /src/core/commands/terminal/shortcut/shortcutCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { defineAsyncComponent } from 'vue' 3 | import ComponentOutputType = WeirdoTerminal.ComponentOutputType 4 | 5 | /** 6 | * 快捷键命令 7 | * @author weirdo 8 | */ 9 | const shortcutCommand: CommandType = { 10 | func: 'shortcut', 11 | name: '快捷键', 12 | desc: '查看快捷键', 13 | alias: [], 14 | params: [], 15 | options: [], 16 | collapsible: true, 17 | action(options, terminal): void { 18 | const output: ComponentOutputType = { 19 | type: 'component', 20 | component: defineAsyncComponent(() => import('./ShortcutBox.vue')) 21 | } 22 | terminal.writeResult(output) 23 | } 24 | } 25 | 26 | export default shortcutCommand 27 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | weirdo-terminal 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /server/src/controller/gptController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * gpt接口 3 | * @param event 4 | * @param req 5 | * @param res 6 | */ 7 | import { getGptOutput } from '../thirdpart/gptApi.js' 8 | import MyError from '../exception/index.js' 9 | import { REQUEST_PARAMS_ERROR_CODE, THIRD_PART_SERVICE_ERROR_CODE } from '../exception/errorCode.js' 10 | async function getGptApi(event, req, res) { 11 | const { message, memory, need } = event 12 | // console.log("message", message); 13 | if (!message) { 14 | throw new MyError(REQUEST_PARAMS_ERROR_CODE, '请输入消息') 15 | } 16 | const result = await getGptOutput(message, memory, need) 17 | if (!result) { 18 | throw new MyError(THIRD_PART_SERVICE_ERROR_CODE) 19 | } 20 | return result 21 | } 22 | 23 | export { getGptApi } 24 | -------------------------------------------------------------------------------- /server/src/thirdpart/varbookApi.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | async function getVarbookOutput(params) { 4 | if (!params) return null 5 | const api = 'https://api-varbook.uiuing.com/translate' 6 | return await axios.get(api, { params }).then((res) => res.data) 7 | // let ob = axios.create({ 8 | // baseURL: 'https://api-varbook.uiuing.com/translate' 9 | // }) 10 | // ob.interceptors.response.use((response) => { 11 | // if (response.status === 200) { 12 | // const { data } = response 13 | // return Promise.resolve(data) 14 | // } 15 | // return Promise.reject(response) 16 | // }) 17 | // let res = await ob.get('', { params }) 18 | // return res 19 | // } 20 | } 21 | export { getVarbookOutput } 22 | -------------------------------------------------------------------------------- /src/core/commands/terminal/welcome/welcomeCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { useTerminalConfigStore } from '@/stores/terminalConfigStore' 3 | 4 | /** 5 | * 自定义终端欢迎语 6 | * @author weirdo 7 | */ 8 | const welcomeCommand: CommandType = { 9 | func: 'welcome', 10 | name: '自定义终端欢迎语', 11 | alias: [], 12 | params: [ 13 | { 14 | key: 'texts', 15 | desc: '终端提示文本(支持多个值,不填则无欢迎语)', 16 | required: false 17 | } 18 | ], 19 | options: [], 20 | async action(options, terminal) { 21 | const { _ } = options 22 | let welcomeTexts = _ // 终端输入 welcome 后面的全是欢迎语 23 | const { setWelcomeTexts } = useTerminalConfigStore() 24 | setWelcomeTexts(welcomeTexts) 25 | } 26 | } 27 | 28 | export default welcomeCommand 29 | -------------------------------------------------------------------------------- /server/src/controller/hotController.js: -------------------------------------------------------------------------------- 1 | // 请求层 : 接收前端发起的请求, 拿到参数 2 | // 请求层调用逻辑层的方法或者调用第三方api 3 | import { getHot } from '../thirdpart/hotApi.js' 4 | import MyError from '../exception/index.js' 5 | import { THIRD_PART_SERVICE_ERROR_CODE, REQUEST_PARAMS_ERROR_CODE } from '../exception/errorCode.js' 6 | 7 | /** 8 | * 随机获取热榜 9 | * @param event 10 | * @param req 11 | * @param res 12 | */ 13 | async function getHotApi(event, req, res) { 14 | // 调用封装的第三方api 15 | const { platform } = event 16 | if (!platform) { 17 | throw new MyError(REQUEST_PARAMS_ERROR_CODE, '请输入相关平台') 18 | } 19 | const result = await getHot(platform) 20 | if (!result) { 21 | throw new MyError(THIRD_PART_SERVICE_ERROR_CODE) 22 | } 23 | return result 24 | } 25 | 26 | export { 27 | getHotApi 28 | } 29 | -------------------------------------------------------------------------------- /server/src/controller/analyzeController.js: -------------------------------------------------------------------------------- 1 | // 请求层 : 接收前端发起的请求, 拿到参数 2 | // 请求层调用逻辑层的方法或者调用第三方api 3 | import { getAnalyze } from '../thirdpart/analyzeApi.js' 4 | import MyError from '../exception/index.js' 5 | import { THIRD_PART_SERVICE_ERROR_CODE, REQUEST_PARAMS_ERROR_CODE } from '../exception/errorCode.js' 6 | 7 | /** 8 | * 网站技术分析 9 | * @param event 10 | * @param req 11 | * @param res 12 | */ 13 | async function getAnalyzeApi(event, req, res) { 14 | // 调用封装的第三方api 15 | const { link } = event 16 | if (!link) { 17 | throw new MyError(REQUEST_PARAMS_ERROR_CODE, '请输入目标链接') 18 | } 19 | const result = await getAnalyze(link) 20 | if (!result) { 21 | throw new MyError(THIRD_PART_SERVICE_ERROR_CODE) 22 | } 23 | return result 24 | } 25 | 26 | export { 27 | getAnalyzeApi 28 | } 29 | -------------------------------------------------------------------------------- /src/core/commands/terminal/help/HelpBox.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue", "src/**/*.ts", "server/src/thirdpart/botApi.js"], 4 | "exclude": ["src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "types": ["node"], 7 | "target": "esnext", 8 | "verbatimModuleSyntax": false, 9 | "useDefineForClassFields": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "strict": true, 13 | "jsx": "preserve", 14 | "sourceMap": true, 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "esModuleInterop": true, 18 | "lib": ["esnext", "dom"], 19 | "skipLibCheck": true, 20 | "composite": true, 21 | "baseUrl": ".", 22 | "paths": { 23 | "@/*": ["./src/*"] 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /server/src/thirdpart/musicApi.js: -------------------------------------------------------------------------------- 1 | // NeteaseCloudMusicApi : 网易云音乐 Node.js API 2 | // https://www.npmjs.com/package/NeteaseCloudMusicApi 3 | import pkg from 'NeteaseCloudMusicApi' 4 | const { cloudsearch } = pkg 5 | 6 | /** 7 | * 搜索音乐 8 | * @param keywords 关键词 9 | * @param limit 分页大小 10 | * @return {Promise<*[]>} 11 | */ 12 | async function searchMusics(keywords, limit = 10) { 13 | if (!keywords) { 14 | return [] 15 | } 16 | try { 17 | const result = await cloudsearch({ 18 | keywords, 19 | type: 1, 20 | limit 21 | }) 22 | if (result.status !== 200) { 23 | return [] 24 | } 25 | const songs = result.body.result.songs 26 | return songs ? songs : [] 27 | } catch (error) { 28 | console.log(error) 29 | return [] 30 | } 31 | } 32 | 33 | export { searchMusics } 34 | -------------------------------------------------------------------------------- /src/core/commands/user/subCommands/logoutCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { userLogin, userLogout, userRegister } from '../userApi' 3 | import { useUserStore } from '@/stores/userStore' 4 | import { LOCAL_USER } from '../userConstant' 5 | 6 | /** 7 | * 用户注销命令 8 | * @author weirdo 9 | */ 10 | const logoutCommand: CommandType = { 11 | func: 'logout', 12 | name: '用户注销', 13 | options: [], 14 | async action(options, terminal) { 15 | const res: any = await userLogout() 16 | const { setLoginUser } = useUserStore() 17 | if (res?.code === 0) { 18 | setLoginUser(LOCAL_USER) 19 | terminal.writeTextSuccessResult('已退出登录') 20 | } else { 21 | terminal.writeTextErrorResult(res?.message ?? '注销失败') 22 | } 23 | } 24 | } 25 | 26 | export default logoutCommand 27 | -------------------------------------------------------------------------------- /src/utils/transferText.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 匹配网址正则 3 | * 将文本中的URL链接转换为HTML超链接。 4 | */ 5 | const URL_REG = 6 | /(((https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/ 7 | 8 | /** 9 | * 识别文本中的超链接 10 | * @param text 11 | */ 12 | const smartText = (text?: string) => { 13 | if (!text) { 14 | return text 15 | } 16 | // 标志"gi"意味着全局(g)和不区分大小写(i)的搜索。 17 | const reg = new RegExp(URL_REG, 'gi') 18 | /** 19 | * 使用replaceAll方法将文本中所有符合正则表达式的URL替换为HTML超链接格式。这里的'$1'是一个捕获组,对应正则表达式中匹配到的整个URL。 20 | 例如,文本中的"http://example.com"会被替换为"http://example.com"。 21 | */ 22 | return text.replaceAll(reg, "$1") 23 | } 24 | 25 | export default smartText 26 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export const plugins = { 2 | 'postcss-px-to-viewport': { 3 | unitToConvert: 'px', // 需要转换的单位,默认为"px" 4 | viewportWidth: 1494, // 设计稿的视口宽度 5 | unitPrecision: 5, // 单位转换后保留的精度 6 | propList: ['*'], // 能转化为vw的属性列表 7 | viewportUnit: 'vw', // 希望使用的视口单位 8 | fontViewportUnit: 'vw', // 字体使用的视口单位 9 | selectorBlackList: [], // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。 10 | minPixelValue: 1, // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换 11 | mediaQuery: false, // 媒体查询里的单位是否需要转换单位 12 | replace: true, // 是否直接更换属性值,而不添加备用属性 13 | exclude: undefined, // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件 14 | include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换 15 | landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape) 16 | landscapeUnit: 'vw', // 横屏时使用的单位 17 | landscapeWidth: 1494 // 横屏时使用的视口宽度 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/src/controller/fanyiController.js: -------------------------------------------------------------------------------- 1 | import { translate } from '../thirdpart/baiduFanYiApi.js' 2 | import MyError from '../exception/index.js' 3 | import { REQUEST_PARAMS_ERROR_CODE, THIRD_PART_SERVICE_ERROR_CODE } from '../exception/errorCode.js' 4 | 5 | /** 6 | * 翻译 7 | * @param event 8 | * @param req 9 | * @param res 10 | */ 11 | 12 | async function translateApi(event, req, res) { 13 | const { keywords, config } = event 14 | // keywords就是要翻译的文本, config里面包括 from 和 to 15 | // 前端的fanyi命令代码 : return await myAxios.post("/fanyi/translate", { keywords, config }); 16 | if (!keywords) { 17 | throw new MyError(REQUEST_PARAMS_ERROR_CODE, '请输入关键词') 18 | } 19 | const result = await translate(keywords, config) 20 | if (!result) { 21 | throw new MyError(THIRD_PART_SERVICE_ERROR_CODE) 22 | } 23 | return result 24 | } 25 | 26 | export { translateApi } 27 | -------------------------------------------------------------------------------- /src/core/commands/search/mdnCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../command' 2 | 3 | const mdnCommand: CommandType = { 4 | func: 'mdn', 5 | name: 'MozillaMDN', 6 | alias: [], 7 | desc: 'mdn <搜索内容>', 8 | params: [ 9 | { 10 | key: 'word', 11 | desc: '搜索内容', 12 | required: true 13 | } 14 | ], 15 | options: [ 16 | { 17 | key: 'self', 18 | desc: '是否当前页面打开', 19 | alias: ['s'], 20 | type: 'boolean', 21 | defaultValue: false 22 | } 23 | ], 24 | action(options, terminal) { 25 | const { _, self } = options 26 | const word = _.length > 0 ? _.join(' ') : '' 27 | const targetLink = `https://developer.mozilla.org/zh-CN/search?q=${word}` 28 | if (self) { 29 | window.location.href = targetLink 30 | } else { 31 | window.open(targetLink) 32 | } 33 | } 34 | } 35 | 36 | export default mdnCommand 37 | -------------------------------------------------------------------------------- /server/src/db/db.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Node.js应用中使用Sequelize库连接到MySQL数据库 3 | */ 4 | 5 | import { Sequelize } from 'sequelize' 6 | import { dbConfig } from '../config/config.js' 7 | 8 | /** 9 | * 创建数据库实例 10 | * @type {Sequelize} 11 | */ 12 | const sequelize = new Sequelize({ 13 | database: dbConfig.database, 14 | username: dbConfig.username, 15 | password: dbConfig.password, 16 | host: dbConfig.host, 17 | port: dbConfig.port, 18 | dialect: 'mysql', // 指定使用的数据库类型 19 | logging: console.log // 指定日志输出函 20 | }) 21 | 22 | // 测试连接 23 | // 使用 .authenticate() 函数测试连接是否正常: 24 | sequelize 25 | .authenticate() 26 | .then(() => { 27 | console.log('MySQL client connected') 28 | }) 29 | .catch((e) => { 30 | console.error('Unable to connect to MySQL', e) 31 | }) 32 | 33 | export default sequelize 34 | 35 | // 默认情况下,Sequelize 将保持连接打开状态,并对所有查询使用相同的连接. 36 | // 如果需要需要关闭连接,请调用 sequelize.close() 37 | -------------------------------------------------------------------------------- /src/core/commands/api/hot/hotCommands.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { getHot } from './hotApi' 3 | 4 | /** 5 | * 热榜相关命令 6 | * @author weirdo 7 | */ 8 | 9 | const hotCommand: CommandType = { 10 | func: 'hot', 11 | name: '资讯热榜前10位', 12 | desc: 'hot zhihu/hupu/baidu/bilibili/history/tieba/webo/douyin/douban/it/...', 13 | params: [ 14 | { 15 | key: 'platform', 16 | desc: '目标平台名称(拼音)', 17 | required: true 18 | } 19 | ], 20 | options: [], 21 | async action(options, terminal) { 22 | const { _ } = options 23 | if (_.length < 1) { 24 | terminal.writeTextErrorResult('参数不足') 25 | return 26 | } 27 | const res: any = await getHot(_.join(' ')) 28 | if (res) { 29 | terminal.writeTextSuccessResult(`请求成功, 以下为热榜信息, 点击链接即可跳转页面。
${res}`) 30 | } else terminal.writeTextErrorResult('请求失败') 31 | } 32 | } 33 | 34 | export default hotCommand 35 | -------------------------------------------------------------------------------- /src/core/commands/relax/moyu/moyuCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { defineAsyncComponent } from 'vue' 3 | import ComponentOutputType = WeirdoTerminal.ComponentOutputType 4 | 5 | /** 6 | * 摸鱼命令 7 | * @author weirdo 8 | */ 9 | const moyuCommand: CommandType = { 10 | func: 'moyu', 11 | name: '摸鱼', 12 | desc: '一些小游戏', 13 | options: [ 14 | { 15 | key: 'back', 16 | type: 'boolean', 17 | desc: '重新进行上一个游戏', 18 | alias: ['b'], 19 | defaultValue: false 20 | } 21 | ], 22 | collapsible: true, 23 | action(options, terminal) { 24 | const { back } = options 25 | const output: ComponentOutputType = { 26 | type: 'component', 27 | // @ts-ignore 28 | component: defineAsyncComponent(() => import('./MoYuBox.vue')), 29 | props: { back } 30 | } 31 | terminal.writeResult(output) 32 | } 33 | } 34 | 35 | export default moyuCommand 36 | -------------------------------------------------------------------------------- /src/core/commands/search/bingCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../command' 2 | 3 | /** 4 | * 必应搜索命令 5 | * @author weirdo 6 | */ 7 | const bingCommand: CommandType = { 8 | func: 'bing', 9 | name: '必应搜索', 10 | alias: [], 11 | desc: 'bing <搜索内容>', 12 | params: [ 13 | { 14 | key: 'word', 15 | desc: '搜索内容', 16 | required: true 17 | } 18 | ], 19 | options: [ 20 | { 21 | key: 'self', 22 | desc: '是否当前页面打开', 23 | alias: ['s'], 24 | type: 'boolean', 25 | defaultValue: false 26 | } 27 | ], 28 | action(options, terminal) { 29 | const { _, self } = options 30 | const word = _.length > 0 ? _.join(' ') : '' 31 | const targetLink = `https://www.bing.com/search?q=${word}` 32 | if (self) { 33 | window.location.href = targetLink 34 | } else { 35 | window.open(targetLink) 36 | } 37 | } 38 | } 39 | 40 | export default bingCommand 41 | -------------------------------------------------------------------------------- /src/core/commands/search/csdnCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../command' 2 | 3 | /** 4 | * 知乎搜索命令 5 | * @author weirdo 6 | */ 7 | const csdnCommand: CommandType = { 8 | func: 'csdn', 9 | name: 'csdn搜索', 10 | alias: [], 11 | desc: 'csdn <搜索内容>', 12 | params: [ 13 | { 14 | key: 'word', 15 | desc: '搜索内容', 16 | required: true 17 | } 18 | ], 19 | options: [ 20 | { 21 | key: 'self', 22 | desc: '是否当前页面打开', 23 | alias: ['s'], 24 | type: 'boolean', 25 | defaultValue: false 26 | } 27 | ], 28 | action(options, terminal) { 29 | const { _, self } = options 30 | const word = _.length > 0 ? _.join(' ') : '' 31 | const targetLink = `https://so.csdn.net/so/search?q=${word}` 32 | if (self) { 33 | window.location.href = targetLink 34 | } else { 35 | window.open(targetLink) 36 | } 37 | } 38 | } 39 | 40 | export default csdnCommand 41 | -------------------------------------------------------------------------------- /src/core/commands/search/douyinCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../command' 2 | 3 | /** 4 | * 抖音搜索命令 5 | * @author weirdo 6 | */ 7 | const douyinCommand: CommandType = { 8 | func: 'douyin', 9 | name: '抖音搜索', 10 | alias: [], 11 | desc: 'douyin <搜索内容>', 12 | params: [ 13 | { 14 | key: 'word', 15 | desc: '搜索内容', 16 | required: true 17 | } 18 | ], 19 | options: [ 20 | { 21 | key: 'self', 22 | desc: '是否当前页面打开', 23 | alias: ['s'], 24 | type: 'boolean', 25 | defaultValue: false 26 | } 27 | ], 28 | action(options, terminal) { 29 | const { _, self } = options 30 | const word = _.length > 0 ? _.join(' ') : '' 31 | const targetLink = `https://www.douyin.com/search/${word}` 32 | if (self) { 33 | window.location.href = targetLink 34 | } else { 35 | window.open(targetLink) 36 | } 37 | } 38 | } 39 | 40 | export default douyinCommand 41 | -------------------------------------------------------------------------------- /src/core/commands/search/zhihuCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../command' 2 | 3 | /** 4 | * 知乎搜索命令 5 | * @author weirdo 6 | */ 7 | const zhihuCommand: CommandType = { 8 | func: 'zhihu', 9 | name: '知乎搜索', 10 | alias: [], 11 | desc: 'zhihu <搜索内容>', 12 | params: [ 13 | { 14 | key: 'word', 15 | desc: '搜索内容', 16 | required: true 17 | } 18 | ], 19 | options: [ 20 | { 21 | key: 'self', 22 | desc: '是否当前页面打开', 23 | alias: ['s'], 24 | type: 'boolean', 25 | defaultValue: false 26 | } 27 | ], 28 | action(options, terminal) { 29 | const { _, self } = options 30 | const word = _.length > 0 ? _.join(' ') : '' 31 | const targetLink = `https://www.zhihu.com/search?q=${word}` 32 | if (self) { 33 | window.location.href = targetLink 34 | } else { 35 | window.open(targetLink) 36 | } 37 | } 38 | } 39 | 40 | export default zhihuCommand 41 | -------------------------------------------------------------------------------- /src/core/commands/space/spaceCommands.ts: -------------------------------------------------------------------------------- 1 | import listCommand from "./listCommand"; 2 | import removeCommand from "./removeCommand"; 3 | import addCommand from "./addCommand"; 4 | import mkdirCommand from "./mkdirCommand"; 5 | import pwdCommand from "./showCommand"; 6 | import cdCommand from "./cdCommand"; 7 | import moveCommand from "./moveCommand"; 8 | import copyCommand from "./copyCommand"; 9 | 10 | /** 11 | * 空间类型(扁平) 12 | */ 13 | export interface SpaceType { 14 | [dir: string]: SpaceItemType; 15 | } 16 | 17 | /** 18 | * 空间项类型 19 | */ 20 | export interface SpaceItemType { 21 | // 条目 / 目录名 22 | name: string; 23 | link?: string; 24 | // 所属目录 25 | dir: string; 26 | type: "dir" | "link"; 27 | } 28 | 29 | /** 30 | * 空间命令,类似文件系统 + 收藏夹 31 | */ 32 | export default [ 33 | listCommand, 34 | removeCommand, 35 | addCommand, 36 | mkdirCommand, 37 | pwdCommand, 38 | cdCommand, 39 | moveCommand, 40 | copyCommand, 41 | ]; 42 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weirdo_terminal_server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "type": "module", 6 | "main": "index.js", 7 | "scripts": { 8 | "start:dev": "cross-env NODE_ENV=local node src/index.js ", 9 | "start": "cross-env NODE_ENV=prod node src/index.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "dependencies": { 13 | "chatgpt": "^5.2.5", 14 | "sequelize": "^6.21.3", 15 | "express": "^4.17.2", 16 | "body-parser": "^1.19.0", 17 | "axios": "^0.27.2", 18 | "md5": "^2.3.0", 19 | "express-session": "^1.17.2", 20 | "redis": "^3.0.2", 21 | "connect-redis": "^6.0.0", 22 | "morgan": "^1.10.0", 23 | "mysql2": "^2.3.3", 24 | "NeteaseCloudMusicApi": "^4.6.7" 25 | }, 26 | "devDependencies": { 27 | "cross-env": "^7.0.3", 28 | "typescript": "^4.4.2" 29 | }, 30 | "author": "weirdo", 31 | "license": "ISC" 32 | } 33 | -------------------------------------------------------------------------------- /src/core/commands/search/doubanCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../command' 2 | 3 | /** 4 | * 豆瓣搜索命令 5 | * @author weirdo 6 | */ 7 | const doubanCommand: CommandType = { 8 | func: 'douban', 9 | name: '豆瓣搜索', 10 | alias: [], 11 | desc: 'douban <搜索内容>', 12 | params: [ 13 | { 14 | key: 'word', 15 | desc: '搜索内容', 16 | required: true 17 | } 18 | ], 19 | options: [ 20 | { 21 | key: 'self', 22 | desc: '是否当前页面打开', 23 | alias: ['s'], 24 | type: 'boolean', 25 | defaultValue: false 26 | } 27 | ], 28 | action(options, terminal) { 29 | const { _, self } = options 30 | const word = _.length > 0 ? _.join(' ') : '' 31 | const targetLink = `https://www.douban.com/search?q=${word}` 32 | if (self) { 33 | window.location.href = targetLink 34 | } else { 35 | window.open(targetLink) 36 | } 37 | } 38 | } 39 | 40 | export default doubanCommand 41 | -------------------------------------------------------------------------------- /src/core/commands/search/juejinCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../command' 2 | 3 | /** 4 | * 知乎搜索命令 5 | * @author weirdo 6 | */ 7 | const juejinCommand: CommandType = { 8 | func: 'juejin', 9 | name: '掘金搜索', 10 | alias: [], 11 | desc: 'juejin <搜索内容>', 12 | params: [ 13 | { 14 | key: 'word', 15 | desc: '搜索内容', 16 | required: true 17 | } 18 | ], 19 | options: [ 20 | { 21 | key: 'self', 22 | desc: '是否当前页面打开', 23 | alias: ['s'], 24 | type: 'boolean', 25 | defaultValue: false 26 | } 27 | ], 28 | action(options, terminal) { 29 | const { _, self } = options 30 | const word = _.length > 0 ? _.join(' ') : '' 31 | const targetLink = `https://juejin.cn/search?query=${word}` 32 | if (self) { 33 | window.location.href = targetLink 34 | } else { 35 | window.open(targetLink) 36 | } 37 | } 38 | } 39 | 40 | export default juejinCommand 41 | -------------------------------------------------------------------------------- /src/core/commands/search/githubCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../command' 2 | 3 | /** 4 | * GitHub 搜索命令 5 | * @author weirdo 6 | */ 7 | const githubCommand: CommandType = { 8 | func: 'github', 9 | name: 'GitHub 搜索', 10 | alias: [], 11 | desc: 'github <搜索内容>', 12 | params: [ 13 | { 14 | key: 'word', 15 | desc: '搜索内容', 16 | required: true 17 | } 18 | ], 19 | options: [ 20 | { 21 | key: 'self', 22 | desc: '是否当前页面打开', 23 | alias: ['s'], 24 | type: 'boolean', 25 | defaultValue: false 26 | } 27 | ], 28 | action(options, terminal) { 29 | const { _, self } = options 30 | const word = _.length > 0 ? _.join(' ') : '' 31 | const targetLink = `https://github.com/search?q=${word}` 32 | if (self) { 33 | window.location.href = targetLink 34 | } else { 35 | window.open(targetLink) 36 | } 37 | } 38 | } 39 | 40 | export default githubCommand 41 | -------------------------------------------------------------------------------- /src/core/commands/search/googleCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../command' 2 | 3 | /** 4 | * Google 搜索命令 5 | * @author weirdo 6 | */ 7 | const googleCommand: CommandType = { 8 | func: 'google', 9 | name: 'Google 搜索', 10 | alias: [], 11 | desc: 'google <搜索内容>', 12 | params: [ 13 | { 14 | key: 'word', 15 | desc: '搜索内容', 16 | required: true 17 | } 18 | ], 19 | options: [ 20 | { 21 | key: 'self', 22 | desc: '是否当前页面打开', 23 | alias: ['s'], 24 | type: 'boolean', 25 | defaultValue: false 26 | } 27 | ], 28 | action(options, terminal) { 29 | const { _, self } = options 30 | const word = _.length > 0 ? _.join(' ') : '' 31 | const targetLink = `https://www.google.com/search?q=${word}` 32 | if (self) { 33 | window.location.href = targetLink 34 | } else { 35 | window.open(targetLink) 36 | } 37 | } 38 | } 39 | 40 | export default googleCommand 41 | -------------------------------------------------------------------------------- /src/core/commands/terminal/reset/resetCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { useTerminalConfigStore } from '@/stores/terminalConfigStore' 3 | 4 | /** 5 | * 重置配置 6 | * @author weirdo 7 | */ 8 | const resetCommand: CommandType = { 9 | func: 'reset', 10 | name: '重置终端配置', 11 | alias: [], 12 | desc: '包括重置主题, 背景, 欢迎语, 是否开启提示', 13 | options: [ 14 | { 15 | key: 'force', 16 | desc: '是否强制重置终端设置', 17 | alias: ['f'], 18 | type: 'boolean', 19 | defaultValue: false 20 | } 21 | ], 22 | action(options, terminal): void { 23 | const { force } = options; 24 | // 从store中拿到重置的方法 25 | const { reset } = useTerminalConfigStore() 26 | if (!force) { 27 | terminal.writeTextErrorResult('操作失败: 请确认要强制重置终端设置') 28 | return 29 | } 30 | reset() 31 | // 重置背景, storage, 欢迎语什么的 32 | terminal.writeTextSuccessResult('已重置终端配置') 33 | } 34 | } 35 | 36 | export default resetCommand 37 | -------------------------------------------------------------------------------- /src/core/commands/search/searchCommands.ts: -------------------------------------------------------------------------------- 1 | import baiduCommand from "./baiduCommand" 2 | import bilibiliCommand from "./bilibiliCommand" 3 | import bingCommand from "./bingCommand" 4 | import csdnCommand from "./csdnCommand" 5 | import doubanCommand from "./doubanCommand" 6 | import douyinCommand from "./douyinCommand" 7 | import githubCommand from "./githubCommand" 8 | import googleCommand from "./googleCommand" 9 | import juejinCommand from "./juejinCommand" 10 | import mdnCommand from "./mdnCommand" 11 | import StackoverflowCommand from "./stackoverflowCommand" 12 | import youtubeCommand from "./youtubeCommand" 13 | import zhihuCommand from "./zhihuCommand" 14 | 15 | export default [ 16 | baiduCommand, 17 | bilibiliCommand, 18 | bingCommand, 19 | csdnCommand, 20 | doubanCommand, 21 | douyinCommand, 22 | githubCommand, 23 | googleCommand, 24 | juejinCommand, 25 | mdnCommand, 26 | StackoverflowCommand, 27 | zhihuCommand, 28 | youtubeCommand, 29 | ] 30 | -------------------------------------------------------------------------------- /src/core/commands/search/stackoverflowCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../command' 2 | 3 | /** 4 | * 掘金搜索命令 5 | * @author weirdo 6 | */ 7 | const StackoverflowCommand: CommandType = { 8 | func: 'stackoverflow', 9 | name: 'Stackoverflow搜索', 10 | desc: 'sf <搜索内容>', 11 | alias: ['sf'], 12 | params: [ 13 | { 14 | key: 'word', 15 | desc: '搜索内容', 16 | required: true 17 | } 18 | ], 19 | options: [ 20 | { 21 | key: 'self', 22 | desc: '是否当前页面打开', 23 | alias: ['s'], 24 | type: 'boolean', 25 | defaultValue: false 26 | } 27 | ], 28 | action(options, terminal) { 29 | const { _, self } = options 30 | const word = _.length > 0 ? _.join(' ') : '' 31 | const targetLink = `https://stackoverflow.com/search?q=${word}` 32 | if (self) { 33 | window.location.href = targetLink 34 | } else { 35 | window.open(targetLink) 36 | } 37 | } 38 | } 39 | 40 | export default StackoverflowCommand 41 | -------------------------------------------------------------------------------- /src/core/commands/user/userApi.ts: -------------------------------------------------------------------------------- 1 | import myAxios from '@/plugins/myAxios' 2 | 3 | /** 4 | * 用户登录 5 | * @param username 6 | * @param password 7 | */ 8 | export const userLogin = async (username: string, password: string) => { 9 | if (!username || !password) { 10 | return null 11 | } 12 | return await myAxios.post('/user/login', { username, password }) 13 | } 14 | 15 | /** 16 | * 用户注销 17 | */ 18 | export const userLogout = async () => { 19 | return await myAxios.post('/user/logout') 20 | } 21 | 22 | /** 23 | * 用户注册 24 | * @param username 25 | * @param password 26 | * @param email 27 | */ 28 | export const userRegister = async (username: string, password: string, email: string) => { 29 | if (!username || !password || !email) { 30 | return null 31 | } 32 | return await myAxios.post('/user/register', { username, password, email }) 33 | } 34 | 35 | /** 36 | * 获取当前登录用户 37 | */ 38 | export const getLoginUser = async () => { 39 | return await myAxios.post('/user/current') 40 | } 41 | -------------------------------------------------------------------------------- /src/core/commands/relax/todo/subCommands/addCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../../command' 2 | import { useTodoStore } from '@/stores/todoStore' 3 | import TaskType = Todo.TaskType 4 | 5 | /** 6 | * 添加任务命令 7 | * @author weirdo 8 | */ 9 | const addCommand: CommandType = { 10 | func: 'add', 11 | name: '添加任务', 12 | options: [ 13 | { 14 | key: 'name', 15 | desc: '任务名称', 16 | alias: ['n'], 17 | type: 'string', 18 | required: true 19 | } 20 | ], 21 | action(options, terminal) { 22 | const { name } = options 23 | if (!name) { 24 | terminal.writeTextErrorResult('请输入任务名称') 25 | return 26 | } 27 | const { addTask } = useTodoStore() 28 | const newTask = { 29 | name 30 | } as TaskType 31 | const res = addTask(newTask) 32 | if (res) { 33 | terminal.writeTextSuccessResult('添加任务成功') 34 | } else { 35 | terminal.writeTextErrorResult('操作失败') 36 | } 37 | } 38 | } 39 | 40 | export default addCommand 41 | -------------------------------------------------------------------------------- /src/core/commands/api/analyze/analyzeApi.ts: -------------------------------------------------------------------------------- 1 | import myAxios from '@/plugins/myAxios' 2 | 3 | function formatTechInfo(obj:any) { 4 | let output = `${obj.title} ${obj.url}
` 5 | output += `* 检测到使用的技术数量: ${obj.total}
` 6 | output += `* 主机信息: ${obj.host.ip} ${obj.host.dz}
` 7 | output += '* 使用的技术
' 8 | 9 | // @ts-ignore 10 | obj.technologies.forEach((tech, index) => { 11 | let techString = `${index + 1}. ${tech.name}` 14 | 15 | if (tech.description) { 16 | techString += `: ${tech.description}` 17 | } 18 | output += `  ${techString}
` 19 | }) 20 | 21 | return output 22 | } 23 | 24 | export const getAnalyze = async (link: string) => { 25 | let res:any = await myAxios.post('/analyze/get', { link }) 26 | if(res.code === 0) { 27 | res = res.data; 28 | res = formatTechInfo(res); 29 | return res; 30 | } else return null; 31 | } 32 | -------------------------------------------------------------------------------- /src/core/commands/relax/todo/todoCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { defineAsyncComponent } from 'vue' 3 | import ComponentOutputType = WeirdoTerminal.ComponentOutputType 4 | import addCommand from './subCommands/addCommand' 5 | 6 | /** 7 | * 待办事项命令 8 | * @author weirdo 9 | */ 10 | const todoCommand: CommandType = { 11 | func: 'todo', 12 | name: '待办事项', 13 | desc: '记录和管理任务', 14 | params: [ 15 | { 16 | key: 'subCommand', 17 | desc: '子命令', 18 | required: true 19 | } 20 | ], 21 | options: [], 22 | subCommands: { 23 | add: addCommand 24 | }, 25 | collapsible: true, 26 | action(options, terminal) { 27 | const { _ } = options 28 | if (_.length < 1) { 29 | const output: ComponentOutputType = { 30 | type: 'component', 31 | component: defineAsyncComponent(() => import('./TodoBox.vue')) 32 | } 33 | terminal.writeResult(output) 34 | return 35 | } 36 | } 37 | } 38 | 39 | export default todoCommand 40 | -------------------------------------------------------------------------------- /src/core/commands/api/music/musicCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { defineAsyncComponent } from 'vue' 3 | import ComponentOutputType = WeirdoTerminal.ComponentOutputType 4 | 5 | /** 6 | * 音乐命令 7 | * @author weirdo 8 | */ 9 | const musicCommand: CommandType = { 10 | func: 'music', 11 | name: '音乐', 12 | desc: '在线听音乐', 13 | params: [ 14 | { 15 | key: 'name', 16 | desc: '音乐名称', 17 | required: true 18 | } 19 | ], 20 | options: [], 21 | collapsible: true, 22 | action(options, terminal) { 23 | const { _ } = options 24 | if (_.length < 1) { 25 | terminal.writeTextErrorResult('参数不足') 26 | return 27 | } 28 | const name = _.join(' ') 29 | const output: ComponentOutputType = { 30 | type: 'component', 31 | component: defineAsyncComponent(() => import('./MusicBox.vue')), 32 | props: { 33 | name 34 | } 35 | } 36 | terminal.writeResult(output) 37 | } 38 | } 39 | 40 | export default musicCommand 41 | -------------------------------------------------------------------------------- /src/stores/userStore.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import { getLoginUser } from '../core/commands/user/userApi' 3 | import { LOCAL_USER } from '../core/commands/user/userConstant' 4 | import UserType = User.UserType 5 | 6 | /** 7 | * 用户系统 8 | */ 9 | export const useUserStore = defineStore('user', { 10 | state: () => ({ 11 | loginUser: { 12 | // 当前登录用户 13 | ...LOCAL_USER 14 | } 15 | }), 16 | getters: {}, 17 | // 持久化 18 | persist: { 19 | key: 'user-store', 20 | storage: window.localStorage, 21 | beforeRestore: (context) => {}, 22 | afterRestore: (context) => {} 23 | }, 24 | actions: { 25 | async getAndSetLoginUser() { 26 | const res: any = await getLoginUser() // 获取当前登录用户 27 | if (res?.code === 0 && res.data) { 28 | this.loginUser = res.data 29 | } else { 30 | // console.warn('未登录或登录失败') 31 | this.$reset() 32 | } 33 | }, 34 | setLoginUser(user: UserType) { 35 | this.loginUser = user 36 | } 37 | } 38 | }) 39 | -------------------------------------------------------------------------------- /src/core/commands/terminal/hint/hintCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from '../../../command' 2 | import { useTerminalConfigStore } from '@/stores/terminalConfigStore' 3 | 4 | /** 5 | * 提示命令 6 | * @author weirdo 7 | */ 8 | const hintCommand: CommandType = { 9 | func: 'hint', 10 | name: '开关提示', 11 | desc: 'hint ', 12 | params: [ 13 | { 14 | key: 'switch', 15 | desc: '开关: on 开启, off 关闭', 16 | defaultValue: 'on' 17 | } 18 | ], 19 | options: [], 20 | async action(options, terminal) { 21 | const { _ } = options 22 | const { setOrToggleShowHint } = useTerminalConfigStore() 23 | let newHint 24 | if (_.length >= 1) { 25 | // 在终端输入 hint 后, 后面你不是要输入 on 或者 off 吗, 就是去匹配 26 | if (['on', 'off'].includes(_[0])) { 27 | newHint = _[0] 28 | } 29 | } 30 | const res = setOrToggleShowHint(newHint) // 如果不传参数或者newHint为undefined则toggle 31 | terminal.writeTextSuccessResult(`输入提示已${res ? '开启' : '关闭'},刷新页面后生效`) 32 | } 33 | } 34 | 35 | export default hintCommand 36 | -------------------------------------------------------------------------------- /src/plugins/myAxios.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | /** 4 | * @ts-ignore 一个特殊的注释,用于告诉TypeScript编译器,忽略某个特定的错误或警告 5 | * 某些情况下会遇到一些TypeScript编译器无法识别或处理的代码,例如第三方库或旧的代码, 6 | * 这时候,编译器可能会产生一些错误或警告,但这些错误或警告并不会影响代码的运行或使用, 7 | * 为了避免这些错误或警告对代码的阅读或编辑造成干扰, 8 | * 可以使用// @ts-ignore注释来告诉编译器忽略这些错误或警告 9 | */ 10 | 11 | let serverAddress = import.meta.env.VITE_SERVER_ADDRESS 12 | 13 | // let baseURL = 'http://localhost:7345/api'; // 开发环境 14 | let baseURL = `https://${serverAddress}/api` // 生产环境 15 | const myAxios = axios.create({ 16 | baseURL 17 | }) 18 | 19 | 20 | myAxios.defaults.withCredentials = true 21 | 22 | myAxios.interceptors.request.use( 23 | function (config) { 24 | return config 25 | }, 26 | function (err) { 27 | return Promise.reject(err) 28 | } 29 | ) 30 | 31 | myAxios.interceptors.response.use( 32 | function (response) { 33 | // console.log(response.data); 34 | return response.data 35 | }, 36 | function (err) { 37 | return Promise.reject(err) 38 | } 39 | ) 40 | 41 | export default myAxios 42 | -------------------------------------------------------------------------------- /src/core/commands/space/cdCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandType } from "../../command"; 2 | import { useSpaceStore } from "@/stores/spaceStore"; 3 | 4 | /** 5 | * 切换目录 6 | */ 7 | const cdCommand: CommandType = { 8 | func: "cd", 9 | name: "切换空间目录", 10 | params: [ 11 | { 12 | key: "dir", 13 | desc: "目标目录", 14 | required: true, 15 | }, 16 | ], 17 | options: [], 18 | action(options, terminal): void { 19 | const { _ } = options; 20 | if (_.length < 1) { 21 | terminal.writeTextErrorResult("参数不足"); 22 | return; 23 | } 24 | const targetDir = _.join(' '); 25 | const spaceStore = useSpaceStore(); 26 | const result = spaceStore.updateCurrentDir(targetDir); 27 | if (result === 1) { 28 | terminal.writeTextErrorResult("已经是根目录,无法切换到上层"); 29 | } else if (result === 2) { 30 | terminal.writeTextErrorResult("切换目录失败: 目录不存在"); 31 | } else if (result === 3) { 32 | terminal.writeTextResult(`已切换至目录:${spaceStore.currentDir}`); 33 | } 34 | }, 35 | }; 36 | 37 | export default cdCommand; 38 | -------------------------------------------------------------------------------- /server/src/thirdpart/baiduFanYiApi.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import MD5 from 'md5' 3 | // import config from '../config/config.js' 4 | import { baiduFanYiConfig } from '../config/config.js' 5 | 6 | const appid = baiduFanYiConfig?.appid 7 | const key = baiduFanYiConfig?.key 8 | const apiUrl = 'http://api.fanyi.baidu.com/api/trans/vip/translate' 9 | 10 | /** 11 | * 百度翻译 12 | * @param keywords 关键词 13 | * @param config 翻译配置 14 | * @return {Promise<*[]>} 15 | */ 16 | async function translate(keywords, config) { 17 | if (!keywords) { 18 | return null 19 | } 20 | const salt = new Date().getTime() 21 | // 多个query可以用\n连接 如 query='apple\norange\nbanana\npear' 22 | const from = config?.from ?? 'auto' 23 | const to = config?.to ?? 'auto' 24 | const sign = MD5(appid + keywords + salt + key) 25 | return await axios({ 26 | method: 'get', 27 | url: apiUrl, 28 | params: { 29 | q: keywords, 30 | appid, 31 | salt, 32 | from, 33 | to, 34 | sign 35 | } 36 | }).then((res) => res.data) 37 | } 38 | 39 | export { translate } 40 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | // /* eslint-env node */ 2 | // require('@rushstack/eslint-patch/modern-module-resolution') 3 | 4 | // module.exports = { 5 | // root: true, 6 | // 'extends': [ 7 | // 'plugin:vue/vue3-essential', 8 | // 'eslint:recommended', 9 | // '@vue/eslint-config-typescript', 10 | // '@vue/eslint-config-prettier/skip-formatting' 11 | // ], 12 | // parserOptions: { 13 | // ecmaVersion: 'latest' 14 | // } 15 | // } 16 | 17 | module.exports = { 18 | env: { 19 | browser: true, 20 | es2021: true, 21 | node: true 22 | }, 23 | extends: [ 24 | 'plugin:vue/vue3-recommended', 25 | 'plugin:prettier/recommended', 26 | 'eslint:recommended', 27 | '@vue/eslint-config-typescript', 28 | '@vue/eslint-config-prettier/skip-formatting' 29 | ], 30 | parserOptions: { 31 | ecmaVersion: 'latest', 32 | parser: '@typescript-eslint/parser', 33 | sourceType: 'module' 34 | }, 35 | plugins: ['vue', '@typescript-eslint', 'prettier'], 36 | rules: { 37 | 'prettier/prettier': 'error', 38 | 'vue/no-v-html': 'off' 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /server/src/controller/musicController.js: -------------------------------------------------------------------------------- 1 | import { getSingleMusic } from '../service/musicService.js' 2 | import MyError from '../exception/index.js' 3 | import { REQUEST_PARAMS_ERROR_CODE, NOT_FOUND_ERROR_CODE } from '../exception/errorCode.js' 4 | 5 | /** 6 | * 获取单首音乐 7 | * @param event 8 | * @param req 9 | * @param res 10 | */ 11 | async function getSingleMusicApi(event, req, res) { 12 | const { keywords } = event 13 | if (!keywords) { 14 | throw new MyError(REQUEST_PARAMS_ERROR_CODE, '请输入关键词') 15 | } 16 | const song = await getSingleMusic(keywords) 17 | if (!song) { 18 | throw new MyError(NOT_FOUND_ERROR_CODE) 19 | } 20 | return { 21 | name: song.name, 22 | id: song.id 23 | } 24 | } 25 | 26 | /** 27 | * 获取歌单详情 28 | * @param event 29 | * @param req 30 | * @param res 31 | */ 32 | // async function getPlaylistDetailApi(event, req, res) { 33 | // const songs = await getPlaylistDetail(); 34 | // if (!songs) { 35 | // throw new MyError(NOT_FOUND_ERROR_CODE); 36 | // } 37 | // return songs; 38 | // } 39 | 40 | export { 41 | getSingleMusicApi 42 | } 43 | -------------------------------------------------------------------------------- /src/core/commands/api/music/MusicBox.vue: -------------------------------------------------------------------------------- 1 |