├── backend
├── __init__.py
├── ai
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-310.pyc
│ │ ├── checktokens.cpython-310.pyc
│ │ ├── get_response.cpython-310.pyc
│ │ └── setaiconfig.cpython-310.pyc
│ ├── checktokens.py
│ ├── setaiconfig.py
│ └── get_response.py
├── entities
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-310.pyc
│ │ └── exceptions.cpython-310.pyc
│ └── exceptions.py
├── route
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── getfile.cpython-310.pyc
│ │ ├── __init__.cpython-310.pyc
│ │ ├── getanswer.cpython-310.pyc
│ │ └── saveanswer.cpython-310.pyc
│ ├── getanswer.py
│ ├── getfile.py
│ └── saveanswer.py
├── service
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── methods.cpython-310.pyc
│ │ ├── __init__.cpython-310.pyc
│ │ └── handle_service.cpython-310.pyc
│ ├── handle_service.py
│ └── methods.py
├── .gitignore
├── app.py
├── db.py
└── requirements.txt
├── assets
├── 1.png
├── 2.png
├── 3.png
└── OIG.jpg
├── frontend
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── assets
│ │ ├── OIG.jpg
│ │ ├── common
│ │ │ └── common.js
│ │ ├── tian.png
│ │ ├── ali_pay.jpg
│ │ ├── genshin.gif
│ │ └── wechat_pay.jpg
│ ├── store
│ │ └── store.js
│ ├── components
│ │ ├── ShowQuestions.vue
│ │ ├── MainContent.vue
│ │ ├── TianliGenshin.vue
│ │ ├── DonateMe.vue
│ │ ├── DetailPage.vue
│ │ ├── LoadingTips.vue
│ │ ├── ClickTips.vue
│ │ ├── FooterChoices.vue
│ │ ├── SearchOldData.vue
│ │ ├── ShowOldData.vue
│ │ ├── GetQuestions.vue
│ │ ├── SaveAnswer.vue
│ │ └── GetSummarize.vue
│ ├── main.js
│ ├── App.vue
│ └── router
│ │ └── index.js
├── babel.config.js
├── vue.config.js
├── .gitignore
├── jsconfig.json
├── README.md
└── package.json
├── .vscode
└── launch.json
├── Dockerfile
└── README.md
/backend/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/backend/ai/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/backend/entities/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/backend/route/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/backend/service/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 | config.py
2 |
3 | /myvenv
4 |
5 | /sql.db
--------------------------------------------------------------------------------
/assets/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/assets/1.png
--------------------------------------------------------------------------------
/assets/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/assets/2.png
--------------------------------------------------------------------------------
/assets/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/assets/3.png
--------------------------------------------------------------------------------
/assets/OIG.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/assets/OIG.jpg
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/src/assets/OIG.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/frontend/src/assets/OIG.jpg
--------------------------------------------------------------------------------
/frontend/src/assets/common/common.js:
--------------------------------------------------------------------------------
1 | const backend_prefix = ''
2 | export default{
3 | backend_prefix
4 | }
--------------------------------------------------------------------------------
/frontend/src/assets/tian.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/frontend/src/assets/tian.png
--------------------------------------------------------------------------------
/frontend/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/src/assets/ali_pay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/frontend/src/assets/ali_pay.jpg
--------------------------------------------------------------------------------
/frontend/src/assets/genshin.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/frontend/src/assets/genshin.gif
--------------------------------------------------------------------------------
/frontend/src/assets/wechat_pay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/frontend/src/assets/wechat_pay.jpg
--------------------------------------------------------------------------------
/backend/ai/__pycache__/__init__.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/backend/ai/__pycache__/__init__.cpython-310.pyc
--------------------------------------------------------------------------------
/backend/route/__pycache__/getfile.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/backend/route/__pycache__/getfile.cpython-310.pyc
--------------------------------------------------------------------------------
/frontend/src/store/store.js:
--------------------------------------------------------------------------------
1 | import {reactive} from 'vue'
2 |
3 | export const store = reactive({
4 | content:'',
5 | currentContent:[],
6 | })
--------------------------------------------------------------------------------
/backend/ai/__pycache__/checktokens.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/backend/ai/__pycache__/checktokens.cpython-310.pyc
--------------------------------------------------------------------------------
/backend/ai/__pycache__/get_response.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/backend/ai/__pycache__/get_response.cpython-310.pyc
--------------------------------------------------------------------------------
/backend/ai/__pycache__/setaiconfig.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/backend/ai/__pycache__/setaiconfig.cpython-310.pyc
--------------------------------------------------------------------------------
/backend/route/__pycache__/__init__.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/backend/route/__pycache__/__init__.cpython-310.pyc
--------------------------------------------------------------------------------
/backend/route/__pycache__/getanswer.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/backend/route/__pycache__/getanswer.cpython-310.pyc
--------------------------------------------------------------------------------
/backend/service/__pycache__/methods.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/backend/service/__pycache__/methods.cpython-310.pyc
--------------------------------------------------------------------------------
/frontend/vue.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('@vue/cli-service')
2 | module.exports = defineConfig({
3 | transpileDependencies: true
4 | })
5 |
--------------------------------------------------------------------------------
/backend/entities/__pycache__/__init__.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/backend/entities/__pycache__/__init__.cpython-310.pyc
--------------------------------------------------------------------------------
/backend/route/__pycache__/saveanswer.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/backend/route/__pycache__/saveanswer.cpython-310.pyc
--------------------------------------------------------------------------------
/backend/service/__pycache__/__init__.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/backend/service/__pycache__/__init__.cpython-310.pyc
--------------------------------------------------------------------------------
/backend/entities/__pycache__/exceptions.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/backend/entities/__pycache__/exceptions.cpython-310.pyc
--------------------------------------------------------------------------------
/backend/service/__pycache__/handle_service.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangcham/ppt_killer/HEAD/backend/service/__pycache__/handle_service.cpython-310.pyc
--------------------------------------------------------------------------------
/frontend/src/components/ShowQuestions.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
--------------------------------------------------------------------------------
/backend/entities/exceptions.py:
--------------------------------------------------------------------------------
1 | class EmptyFileError(Exception):
2 | pass
3 |
4 | class ImageTransferError(Exception):
5 | pass
6 |
7 | class GetContentError(Exception):
8 | pass
9 |
10 | class ChatgptGenerateError(Exception):
11 | pass
12 |
13 | class SqliteError(Exception):
14 | pass
--------------------------------------------------------------------------------
/frontend/src/components/MainContent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
--------------------------------------------------------------------------------
/frontend/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "esnext",
5 | "baseUrl": "./",
6 | "moduleResolution": "node",
7 | "paths": {
8 | "@/*": [
9 | "src/*"
10 | ]
11 | },
12 | "lib": [
13 | "esnext",
14 | "dom",
15 | "dom.iterable",
16 | "scripthost"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/src/components/TianliGenshin.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 |
6 |
7 |
18 |
19 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # frontend
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Lints and fixes files
19 | ```
20 | npm run lint
21 | ```
22 |
23 | ### Customize configuration
24 | See [Configuration Reference](https://cli.vuejs.org/config/).
25 |
--------------------------------------------------------------------------------
/backend/ai/checktokens.py:
--------------------------------------------------------------------------------
1 | import tiktoken
2 |
3 | class CheckTokens:
4 | def get_num_tokens(self,text) -> int:
5 | encoding = tiktoken.get_encoding("cl100k_base")
6 | encoding = tiktoken.encoding_for_model("gpt-3.5-turbo")
7 |
8 | num_tokens = len(encoding.encode(text))
9 | print(num_tokens)
10 | if num_tokens >= 3500:
11 | return 1
12 | else:
13 | return 2
14 |
15 |
--------------------------------------------------------------------------------
/backend/ai/setaiconfig.py:
--------------------------------------------------------------------------------
1 | import openai
2 | import os
3 |
4 | def setconfig():
5 |
6 | # openai.proxy = "http://127.0.0.1:7890"
7 | # openai.api_key = config.openai_api_key
8 |
9 |
10 |
11 | if os.getenv('API_BASE') == '':
12 | openai.api_key = os.environ.get('API_KEY',"None")
13 | print("环境变量"+openai.api_key)
14 | else:
15 | openai.api_key = os.getenv('API_KEY')
16 | openai.api_base = os.getenv('API_BASE')
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // 使用 IntelliSense 了解相关属性。
3 | // 悬停以查看现有属性的描述。
4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "chrome",
9 | "request": "launch",
10 | "name": "针对 localhost 启动 Chrome",
11 | "url": "http://localhost:8080",
12 | "webRoot": "${workspaceFolder}"
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 | import ElementPlus from 'element-plus'
4 | import 'element-plus/dist/index.css'
5 | import * as ElementPlusIconsVue from '@element-plus/icons-vue'
6 | import router from './router/index'
7 |
8 | const app = createApp(App)
9 | app.use(ElementPlus)
10 | for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
11 | app.component(key, component)
12 | }
13 | app.use(router)
14 | app.mount('#app')
15 |
16 |
--------------------------------------------------------------------------------
/frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
22 |
23 | >
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:latest
2 |
3 | RUN apt-get update -y
4 |
5 | RUN apt-get install -y python3.9 python3-pip
6 |
7 | RUN apt-get install -y tesseract-ocr
8 |
9 | RUN apt-get install -y wget
10 | RUN wget https://github.com/tesseract-ocr/tessdata_best/blob/main/chi_sim.traineddata
11 | RUN mv chi_sim.traineddata /usr/share/tesseract-ocr/4.00/tessdata/
12 |
13 | WORKDIR /app
14 |
15 | COPY backend ./backend
16 | COPY frontend/dist ./frontend/dist
17 |
18 | RUN pip3 install -r backend/requirements.txt
19 |
20 | EXPOSE 5000
21 |
22 | ENV API_KEY=""
23 | ENV API_BASE=""
24 |
25 | CMD [ "python3","backend/app.py" ]
--------------------------------------------------------------------------------
/frontend/src/components/DonateMe.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
是这样的,服务器和Api Key都是需要定期充值的,捐赠的钱会用来充值这些必需品,来支持后续的服务
4 |
5 |

6 |

7 |
8 |
9 |
10 |
11 |
16 |
17 |
--------------------------------------------------------------------------------
/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/frontend/src/components/DetailPage.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
30 |
31 |
--------------------------------------------------------------------------------
/frontend/src/router/index.js:
--------------------------------------------------------------------------------
1 | import {createRouter,createWebHistory} from 'vue-router'
2 | import GetSummarize from '../components/GetSummarize.vue'
3 | import SearchOldData from '../components/SearchOldData.vue'
4 | import DetailPage from '../components/DetailPage.vue'
5 | import ShowOldData from '../components/ShowOldData.vue'
6 | const routes = [
7 | { path :'/',redirect:'/GetSummarize'},
8 | { path :'/GetSummarize',name:'/GetSummarize',components:{GetSummarize:GetSummarize}},
9 | { path : '/SearchOldData',name:'/SearchOldData',components:{SearchOldData:SearchOldData}},
10 | { path : '/DetailPage',name:'/DetailPage',components:{DetailPage:DetailPage}},
11 | { path:'/ShowOldData',name:'/ShowOldData',components:{ShowOldData:ShowOldData}},
12 | ]
13 |
14 | const router = createRouter({
15 | history: createWebHistory(),
16 | routes
17 | });
18 |
19 | export default router
--------------------------------------------------------------------------------
/frontend/src/components/LoadingTips.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
提示:
5 |
如果上面的蓝色方框一直显示正在加载,不要着急,耐心等待一会儿,请不要关闭网页。
6 |
AI生成回答之后,会弹出消息提示框,并且在蓝色方框下方生成MarkDown格式内容。
7 |
有些时候由于服务器和网络的原因,AI回复的速度会变得很慢,请保持耐心。
8 |
MarkDown格式的效果类似这种的:
9 |
我很喜欢U2乐队,为什么呢?
10 |
11 | - 因为他们的音乐很牛逼
12 | - 因为我喜欢玩原神
13 | - 因为我不是二次元
14 |
15 |
这就是得到的效果,在得到AI回复后,本提示将会马上消失,祝你使用愉快!
16 |
17 |
18 |
19 |
20 |
25 |
26 |
--------------------------------------------------------------------------------
/backend/app.py:
--------------------------------------------------------------------------------
1 | from quart import send_from_directory, render_template
2 | from quart import Quart, request, Blueprint
3 | from route.getfile import getfile_app
4 | from route.getanswer import getanswer_app
5 | from route.saveanswer import saveanswer_app
6 | import os
7 | import asyncio
8 | import db
9 |
10 | app = Quart(__name__, template_folder="../frontend/dist", static_folder="../frontend/dist", static_url_path="")
11 |
12 | app.register_blueprint(getfile_app)
13 | app.register_blueprint(getanswer_app)
14 | app.register_blueprint(saveanswer_app)
15 |
16 | @app.route('/')
17 | async def index():
18 | return await render_template("index.html")
19 |
20 | if __name__ == '__main__':
21 | loop = asyncio.get_event_loop()
22 | try:
23 | database = db.database()
24 | database.create()
25 | loop.run_until_complete(app.run_task(host='0.0.0.0', port=5000))
26 | except KeyboardInterrupt:
27 | pass
28 | finally:
29 | loop.close()
--------------------------------------------------------------------------------
/backend/route/getanswer.py:
--------------------------------------------------------------------------------
1 | from quart import Blueprint,request,jsonify,Quart
2 | from quart_cors import cors
3 | import db
4 | from entities.exceptions import SqliteError
5 |
6 | getanswer_app = Blueprint('getanswer_app',__name__)
7 | cors(getanswer_app)
8 | @getanswer_app.route('/olddata',methods=['post'])
9 | async def olddata():
10 | data = await request.get_json()
11 | token = data['content']
12 | try:
13 | database = db.database()
14 | sql = "select type,content from info where token = ?"
15 | results = database.execute(sql,(token,))
16 |
17 | if not results:
18 | return jsonify({'status':'fail','message':'未查询到符合该密钥的结果'})
19 | else:
20 | data = []
21 | for result in results:
22 | type = result[0]
23 | content = result[1]
24 |
25 | item = {'type':type,'content':content}
26 | data.append(item)
27 |
28 | return jsonify(data)
29 | except SqliteError:
30 | return jsonify({'status':'fail','message':'数据库操作失败'})
--------------------------------------------------------------------------------
/backend/db.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 | #create table
3 | table_one = """
4 | CREATE TABLE if not exists info(
5 | token VARCHAR(255),
6 | type INT,
7 | content TEXT
8 | )
9 | """
10 |
11 |
12 | class database:
13 | def execute(self,query,args=None):
14 | self.conn = None
15 | self.cursor = None
16 | try:
17 | self.conn = sqlite3.connect('sql.db')
18 | self.cursor = self.conn.cursor()
19 |
20 | if args:
21 | self.cursor.execute(query,args)
22 | else:
23 | self.cursor.execute(query)
24 |
25 | results = self.cursor.fetchall()
26 | self.conn.commit()
27 |
28 | return results
29 |
30 | except Exception as e:
31 | print(e)
32 | if self.conn:
33 | self.conn.rollback()
34 |
35 |
36 | def close(self):
37 | if self.conn:
38 | self.conn.close()
39 |
40 | def create(self):
41 | self.execute(table_one)
42 | print("成功建表")
43 |
44 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "@element-plus/icons-vue": "^2.1.0",
12 | "axios": "^1.5.1",
13 | "core-js": "^3.8.3",
14 | "element-plus": "^2.4.0",
15 | "libgif": "^0.0.3",
16 | "markdown-it": "^13.0.2",
17 | "vue": "^3.2.13",
18 | "vue-router": "^4.2.5"
19 | },
20 | "devDependencies": {
21 | "@babel/core": "^7.12.16",
22 | "@babel/eslint-parser": "^7.12.16",
23 | "@types/markdown-it": "^13.0.2",
24 | "@vue/cli-plugin-babel": "~5.0.0",
25 | "@vue/cli-plugin-eslint": "~5.0.0",
26 | "@vue/cli-service": "~5.0.0",
27 | "eslint": "^7.32.0",
28 | "eslint-plugin-vue": "^8.0.3"
29 | },
30 | "eslintConfig": {
31 | "root": true,
32 | "env": {
33 | "node": true
34 | },
35 | "extends": [
36 | "plugin:vue/vue3-essential",
37 | "eslint:recommended"
38 | ],
39 | "parserOptions": {
40 | "parser": "@babel/eslint-parser"
41 | },
42 | "rules": {}
43 | },
44 | "browserslist": [
45 | "> 1%",
46 | "last 2 versions",
47 | "not dead",
48 | "not ie 11"
49 | ]
50 | }
51 |
--------------------------------------------------------------------------------
/backend/service/handle_service.py:
--------------------------------------------------------------------------------
1 | import os
2 | from typing import IO
3 | from . import methods
4 | from quart import jsonify
5 |
6 | ALLOWED_EXTENSIONS = {'ppt','pptx'}
7 |
8 | def allowed_file(filename):
9 | return '.' in filename and \
10 | filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
11 |
12 |
13 | class handle_file:
14 | def __init__(self,file:IO):
15 | self.file = file
16 |
17 | async def checkext(self):
18 | #在这里检查文件格式
19 | if not allowed_file(self.file.filename):
20 | return jsonify({'status':'fail','message':'不是允许的文件类型'})
21 |
22 | ext = os.path.splitext(self.file.filename)[1]
23 | print('接受到的文件拓展名:'+ext)
24 | self.ext = ext
25 |
26 | async def handle(self):
27 | if self.file:
28 | print("文件存在")
29 |
30 | method = methods.GetText(self.file)
31 |
32 | if(self.ext == '.pptx'):
33 | print("进入pptx文件使用")
34 | result =await method.pptx_method()
35 | elif(self.ext == '.ppt'):
36 | result =await method.ppt_method()
37 |
38 | return result
39 |
40 |
41 | @classmethod
42 | async def create(cls,file:IO):
43 | instance = cls(file)
44 | await instance.checkext()
45 | return instance
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/backend/route/getfile.py:
--------------------------------------------------------------------------------
1 | import traceback
2 | from quart import request,Blueprint,jsonify
3 | from quart_cors import cors
4 | import service.handle_service as hs
5 | from entities.exceptions import GetContentError,EmptyFileError
6 | import ai.get_response as gr
7 |
8 |
9 | getfile_app = Blueprint('getfile_app',__name__)
10 | cors(getfile_app)
11 |
12 | @getfile_app.route('/getfile',methods=['post'])
13 | async def getfile():
14 | try:
15 | files = await request.files
16 | file = files.get('file', None)
17 |
18 | if file:
19 | print('后端接受到文件')
20 | file_handler =await hs.handle_file.create(file)
21 | response = await file_handler.handle()
22 | print("进行操作,得到ppt")
23 | return response
24 | else:
25 | return jsonify({'status':'fail','message':'接受文件失败','code':'0'})
26 | except EmptyFileError:
27 | print(traceback.format_exc())
28 | return jsonify({'status':'fail','message':'抛出异常'})
29 |
30 | @getfile_app.route('/getques',methods=['post'])
31 | async def getques():
32 | try:
33 | data = await request.get_json()
34 | content = data['content']
35 | #编写逻辑
36 | if content:
37 | getres = gr.response(content)
38 | result = await getres.get_generated_answer(content)
39 |
40 | return result
41 | else:
42 | return jsonify({'status':'fail','message':'获得的内容为空'})
43 |
44 | except GetContentError as e:
45 | return jsonify({'status':'fail','message':'获得内容失败'})
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/backend/route/saveanswer.py:
--------------------------------------------------------------------------------
1 | from quart import Quart,request,jsonify,Blueprint
2 | from quart_cors import cors
3 | import db
4 | from entities.exceptions import SqliteError
5 | saveanswer_app = Blueprint('saveanswer_app',__name__)
6 |
7 | cors(saveanswer_app)
8 |
9 | @saveanswer_app.route('/saveanswer',methods=['post'])
10 | async def saveanswer():
11 | data = await request.get_json()
12 | content = data['content']
13 | type = data['type']
14 | token = data['token']
15 |
16 | sql1 = "select * from info where token = ?"
17 | database = db.database()
18 | result = database.execute(sql1,(token,))
19 | if not result:
20 | return jsonify({'code':'1'})
21 | else:
22 | sql2 = "INSERT INTO info (token,type,content) VALUES (?,?,?)"
23 | try:
24 | result = database.execute(sql2,(token,type,content,))
25 | return jsonify({'status':'success','code':'3'})
26 | except SqliteError:
27 | print("数据库插入操作失败")
28 | return jsonify({'status':'fail','message':'数据库插入操作失败','code':'2'})
29 |
30 | @saveanswer_app.route('/saveagain',methods=['post'])
31 | async def saveagain():
32 | data = await request.get_json()
33 | content = data['content']
34 | type = data['type']
35 | token = data['token']
36 |
37 | sql2 = "INSERT INTO info (token,type,content) VALUES (?,?,?)"
38 | try:
39 | database = db.database()
40 | database.execute(sql2,(token,type,content,))
41 | return jsonify({'status':'success','message':'保存成功'})
42 | except SqliteError:
43 | return jsonify({'status':'fail','message':'数据库操作失败'})
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 |
5 | # PPT-killer
6 |
7 | 点击使用[公共体验服务](https://www.pptkiller.top)
8 |
9 | ## 功能
10 |
11 | 1. 支持将pptx文件投喂给chatgpt,总结pptx的内容
12 | 2. 支持根据AI的总结生成与pptx文件相关的问题
13 | 3. 支持docker部署并填写自己的API_key和API_base
14 | 4. 可选择是否保存AI生成的总结与问题
15 |
16 | ## 部署
17 |
18 | ### 基于docker部署(推荐)
19 |
20 | #### 配置项:
21 |
22 | API_KEY : 对应自己的api key(必填)
23 | API_BASE : 对应自己的base url(可填可不填)
24 |
25 | ```
26 | #拉取docker镜像
27 | docker pull wangcham/pptkiller
28 |
29 | #启动!(可加入环境变量API_BASE,本条命令没有加)
30 | docker run -it -d -p 5000:5000 -e API_KEY=your-api-key --name pptkiller wangcham/pptkiller
31 | ```
32 |
33 | ### 手动部署
34 |
35 | ```
36 | #拉取源代码(国内用户可使用gitee地址拉取代码)
37 | git clone https://github.com/wangcham/ppt_killer.git
38 |
39 | #进入前端
40 | cd ppt_killer/frontend
41 |
42 | npm install
43 |
44 | npm run build
45 |
46 | #进入后端
47 | cd ppt_killer/backend
48 | pip install -r requirements.txt
49 | python3 app.py
50 | ```
51 |
52 | 强烈推荐使用docker部署!
53 |
54 | 安装好之后,在浏览器打开IP地址:5000就可以看到部署成功的pptkiller
55 |
56 |
57 | ### 建议配合free-one-api与one-api食用
58 |
59 | 这两个仓库是管理api key的,其中free-one-api为逆向库。
60 | 查看 [free-one-api](https://github.com/RockChinQ/free-one-api)
61 | 查看 [one-api](https://github.com/songquanpeng/one-api/)
62 | 目前one-api使用可能出现报错http code 200的问题,谨慎使用!(可能是使用的openai库版本较低的原因,后续会修复以配合one-api)
63 |
64 |
65 |
66 | ## 网站截图
67 |
68 |
69 |
70 |
71 |
72 |
73 | ## 后续会支持的功能
74 | 1. 支持将文本切分成小段,以加快响应速度
75 | 2. 可供用户选择的其他模型与配置
76 | 3. 页面优化
77 |
78 | ## 特别感谢
79 | @[theLazy](https://thelazy.cn/)设计的天理原神启动页动画,素材在[此仓库](https://github.com/the-lazy-me/sourceBox)中
80 |
--------------------------------------------------------------------------------
/frontend/src/components/ClickTips.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 如果上面的蓝色方框一直显示正在加载,不要着急,耐心等待一会儿,请不要关闭网页。
6 | AI生成回答之后,会弹出消息提示框,并且在蓝色方框下方生成MarkDown格式内容。
7 | 有些时候由于服务器和网络的原因(或者上传的文件很大),AI回复的速度会变得很慢,请保持耐心。
8 | MarkDown格式的效果类似这种的:
9 |
10 | 我很喜欢U2乐队,为什么呢?
11 |
12 | - 因为他们的音乐很牛逼
13 | - 因为我喜欢玩原神
14 | - 因为我不是二次元
15 |
16 |
17 | 以上就是得到的效果
18 | 如果你只有ppt文件,那么请使用外部网站或者Microsoft Powerpoint
19 | 将ppt文件转换成pptx文件,外部网站可以自己搜索去转换文件。
20 | !本站不保存用户上传的任何文件!
21 |
22 |
23 | 是这样的,在生成内容之后,可以选择保存AI生成的回复
24 | 你可以选择输入的专属密钥(相当于令牌),密钥网站会替你保存,在你下次想要看自己生成的内容时,
25 | 可以在上方输入你的密钥,这时,网站就可以检索出属于你的令牌的生成的内容
26 | 不要担心是否会发生令牌重复的问题,网站会检查一切。
27 | 提示到这里就结束了,你很聪明,因为至少在使用前会去看看说明,感谢使用!
28 |
29 |
30 |
31 |
32 |
33 |
43 |
44 |
--------------------------------------------------------------------------------
/frontend/src/components/FooterChoices.vue:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 |
52 |
53 |
--------------------------------------------------------------------------------
/frontend/src/components/SearchOldData.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
55 |
56 |
--------------------------------------------------------------------------------
/backend/service/methods.py:
--------------------------------------------------------------------------------
1 | import io
2 | import traceback
3 | from typing import IO
4 | from pptx import Presentation
5 | from pptx.util import Inches,Pt
6 | import pytesseract
7 | from PIL import Image
8 | import ai.get_response
9 |
10 |
11 |
12 | class GetText:
13 | def __init__(self,file:IO):
14 | self.file = file
15 |
16 |
17 | async def pptx_method(self):
18 | text = ""
19 | presentation = Presentation(self.file)
20 | print("pptxmethod内部")
21 | try:
22 | for slide in presentation.slides:
23 | for shape in slide.shapes:
24 | if hasattr(shape, 'text'):
25 | text += shape.text + '\n'
26 | if shape.shape_type == 13:
27 | image = shape.image
28 | try:
29 | img = Image.open(io.BytesIO(image.blob))
30 | img = img.convert('L')
31 | img_text = pytesseract.image_to_string(img)
32 | text += img_text + '\n'
33 | except Exception as e:
34 | print("有图片异常")
35 | # 检查是否有text
36 | if not text:
37 | print("no text")
38 |
39 | self.text = text
40 | #存数据库
41 | print("pptx发送ai")
42 | result = await self.send_to_ai(self.text)
43 | return result
44 |
45 | except Exception as e:
46 | print("读取文件内容异常")
47 | print(traceback.format_exc())
48 | return "读取文件异常"
49 |
50 | async def ppt_method(self):
51 | print("ppt file")
52 | return "无法解析ppt文件,正在加紧开发,请尝试使用其他网站转换文件格式或者使用PowerPoint将ppt文件改成pptx文件再使用"
53 |
54 | async def send_to_ai(self,alltext):
55 | print("send to ai")
56 | operate =ai.get_response.response(alltext)
57 | result = await operate.complete()
58 | return result
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/backend/ai/get_response.py:
--------------------------------------------------------------------------------
1 | from . import checktokens
2 | import os
3 | import traceback
4 | import openai
5 | from quart import jsonify
6 | from entities.exceptions import ChatgptGenerateError
7 | from . import setaiconfig
8 | class response:
9 | def __init__(self, text):
10 | self.text = text
11 |
12 | async def complete(self):
13 | try:
14 |
15 | setaiconfig.setconfig()
16 |
17 | check = checktokens.CheckTokens()
18 | type = check.get_num_tokens(self.text)
19 | if type == 1:
20 | used_model = "gpt-3.5-turbo-16k"
21 | print("调用gpt16k")
22 | if type == 2:
23 | used_model = "gpt-3.5-turbo"
24 | print("调用gpt3.5turbo接口")
25 |
26 | completion = openai.ChatCompletion.create(
27 | model=used_model,
28 | messages=[
29 | {"role": "system", "content": "你是一个帮助人们总结内容的助手"},
30 | {"role": "user", "content": "请给我总结这些内容,我给你的文本的内容是乱的,请你认真思考一下,把他整理清楚,给出我中文的回复,不允许省略回复,以分层标题等的形式生成大纲,另外,生成的回答必须是markdown格式,记得写成垂直方向的换行,也就是需要你加换行的标志,让他形式好看一点,并且请翻译成中文:" + self.text}
31 | ]
32 | )
33 | self.result = completion.choices[0].message['content']
34 | print(self.result)
35 | return jsonify({'status':'success','result':self.result})
36 | except Exception as e:
37 | print(traceback.format_exc())
38 | self.result = "#### chatgpt响应异常,异常是" + str(e) + "请稍后使用网站~"
39 | return jsonify({'status':'fail','result':self.result})
40 |
41 | async def get_generated_answer(self,ques_text):
42 |
43 | setaiconfig.setconfig()
44 |
45 | check = checktokens.CheckTokens()
46 | type = check.get_num_tokens(ques_text)
47 |
48 | if type == 1:
49 | used_model = "gpt-3.5-turbo-16k"
50 | if type == 2:
51 | used_model = "gpt-3.5-turbo"
52 |
53 | try:
54 | completion = openai.ChatCompletion.create(
55 | model=used_model,
56 | messages=[
57 | {"role": "system", "content": "你是一个帮助人们理解PPT的助手"},
58 | {"role": "user", "content": "请将这些杂乱的文本整理清楚,然后依据这些内容生成大约二三十道问题,并且在最后给出答案,然后必须以markdown的形式发送给我,文本内容是:" + ques_text}
59 | ]
60 | )
61 | result = completion.choices[0].message['content']
62 | return jsonify({'status':'success','result':result})
63 | except ChatgptGenerateError:
64 | return jsonify({'status':'fail','message':'chatgpt生成内容失败'})
--------------------------------------------------------------------------------
/backend/requirements.txt:
--------------------------------------------------------------------------------
1 | #
2 | # This file is autogenerated by pip-compile with Python 3.10
3 | # by the following command:
4 | #
5 | # pip-compile requirements.in
6 | #
7 | --index-url https://pypi.tuna.tsinghua.edu.cn/simple
8 |
9 | aiofiles==23.2.1
10 | # via quart
11 | annotated-types==0.6.0
12 | # via pydantic
13 | anyio==3.7.1
14 | # via
15 | # httpx
16 | # openai
17 | blinker==1.7.0
18 | # via
19 | # flask
20 | # quart
21 | certifi==2023.7.22
22 | # via
23 | # httpcore
24 | # httpx
25 | # requests
26 | charset-normalizer==3.3.2
27 | # via requests
28 | click==8.1.7
29 | # via
30 | # flask
31 | # quart
32 | colorama==0.4.6
33 | # via
34 | # click
35 | # tqdm
36 | distro==1.8.0
37 | # via openai
38 | exceptiongroup==1.1.3
39 | # via
40 | # anyio
41 | # taskgroup
42 | flask==3.0.0
43 | # via quart
44 | h11==0.14.0
45 | # via
46 | # httpcore
47 | # hypercorn
48 | # wsproto
49 | h2==4.1.0
50 | # via hypercorn
51 | hpack==4.0.0
52 | # via h2
53 | httpcore==1.0.1
54 | # via httpx
55 | httpx==0.25.1
56 | # via openai
57 | hypercorn==0.15.0
58 | # via quart
59 | hyperframe==6.0.1
60 | # via h2
61 | idna==3.4
62 | # via
63 | # anyio
64 | # httpx
65 | # requests
66 | itsdangerous==2.1.2
67 | # via
68 | # flask
69 | # quart
70 | jinja2==3.1.2
71 | # via
72 | # flask
73 | # quart
74 | lxml==4.9.3
75 | # via python-pptx
76 | markupsafe==2.1.3
77 | # via
78 | # jinja2
79 | # quart
80 | # werkzeug
81 | openai==0.28
82 | # via -r requirements.in
83 | packaging==23.2
84 | # via pytesseract
85 | pillow==10.1.0
86 | # via
87 | # -r requirements.in
88 | # pytesseract
89 | # python-pptx
90 | priority==2.0.0
91 | # via hypercorn
92 | pydantic==2.4.2
93 | # via openai
94 | pydantic-core==2.10.1
95 | # via pydantic
96 | pytesseract==0.3.10
97 | # via -r requirements.in
98 | python-pptx==0.6.23
99 | # via -r requirements.in
100 | quart==0.19.3
101 | # via
102 | # -r requirements.in
103 | # quart-cors
104 | quart-cors==0.7.0
105 | # via -r requirements.in
106 | regex==2023.10.3
107 | # via tiktoken
108 | requests==2.31.0
109 | # via tiktoken
110 | sniffio==1.3.0
111 | # via
112 | # anyio
113 | # httpx
114 | taskgroup==0.0.0a4
115 | # via hypercorn
116 | tiktoken==0.5.1
117 | # via -r requirements.in
118 | tomli==2.0.1
119 | # via hypercorn
120 | tqdm==4.66.1
121 | # via openai
122 | typing-extensions==4.8.0
123 | # via
124 | # openai
125 | # pydantic
126 | # pydantic-core
127 | urllib3==2.0.7
128 | # via requests
129 | werkzeug==3.0.1
130 | # via
131 | # flask
132 | # quart
133 | wsproto==1.2.0
134 | # via hypercorn
135 | xlsxwriter==3.1.9
136 | # via python-pptx
137 |
--------------------------------------------------------------------------------
/frontend/src/components/ShowOldData.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |

6 |
10 |
11 |
搜索到的结果:
12 |
13 |
14 |
15 |
16 |
{{ result.type === 1 ? '总结':'问题' }}
17 | : {{ result.content.length > 60 ? result.content.substring(0, 60) + '...' : result.content }}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
60 |
61 |
--------------------------------------------------------------------------------
/frontend/src/components/GetQuestions.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 点击使AI生成问题
5 | 重新生成问题
6 |
7 |
8 |
9 |
10 |
11 | {{ message }}
12 |
13 |
14 |
15 |
16 | 保存回答
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
116 |
117 |
--------------------------------------------------------------------------------
/frontend/src/components/SaveAnswer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
提交
5 |
6 |
7 |
8 | 确认
9 | 取消
10 |
11 |
12 |
13 |
14 |
15 |
16 |
115 |
116 |
--------------------------------------------------------------------------------
/frontend/src/components/GetSummarize.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |

8 |
12 |
13 |
14 |
15 |
16 |
17 |
25 |
26 |
27 |
28 | 已选择文件:{{selectedFileName}}
29 |
30 |
31 |
32 | 询问AI
33 | 清除内容
34 |
35 |
36 |
37 | 发生错误 {{this.message}}
38 |
39 | 重新开始
40 |
41 |
42 | 重新开始
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
53 |
54 |
55 | 保存总结
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
212 |
213 |
--------------------------------------------------------------------------------