0x-YuAN commited on
Commit
ca366fb
·
verified ·
1 Parent(s): 36b7167

Upload 5 files

Browse files
Files changed (5) hide show
  1. app.py +176 -0
  2. chat_analysis.py +49 -0
  3. img2chat.py +96 -0
  4. index.html +557 -0
  5. requirements.txt +6 -0
app.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Flask backend for the chat assistance website.
3
+
4
+ This application provides API endpoints for processing chat screenshots,
5
+ manual text input, and generating response suggestions.
6
+ """
7
+ import os
8
+ import base64
9
+ import json
10
+ from flask import Flask, request, jsonify, send_file
11
+ from flask_cors import CORS
12
+ from werkzeug.utils import secure_filename
13
+ from dotenv import load_dotenv
14
+ from img2chat import img2chat
15
+ from chat_analysis import get_suggestion
16
+
17
+ # Load environment variables
18
+ load_dotenv()
19
+
20
+ # Initialize Flask app
21
+ app = Flask(__name__)
22
+ CORS(app) # Enable CORS for all routes
23
+
24
+ # Configure upload folder for images
25
+ UPLOAD_FOLDER = 'uploads'
26
+ ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
27
+ app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
28
+ app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max upload size
29
+
30
+ # Create uploads directory if it doesn't exist
31
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
32
+
33
+ def allowed_file(filename):
34
+ """Check if the file extension is allowed"""
35
+ return '.' in filename and \
36
+ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
37
+
38
+ def generate_response(chat_text):
39
+ """
40
+ Generate response suggestions based on chat text.
41
+
42
+ Uses the get_suggestion function from chat_analysis.py to generate
43
+ response suggestions based on the provided chat text.
44
+
45
+ Args:
46
+ chat_text (str): The chat text to analyze
47
+
48
+ Returns:
49
+ dict: Analysis and response suggestions
50
+ """
51
+ # Get suggestion from chat_analysis module
52
+ suggestion_text = get_suggestion(chat_text)
53
+
54
+ # Parse the XML response to extract suggestion and example
55
+ # The response format is expected to be:
56
+ # <suggestion>Suggestion text</suggestion><example>Example text</example>
57
+
58
+ analysis = "分析中..."
59
+ suggestion = suggestion_text
60
+
61
+ # Simple parsing of XML tags (could be improved with proper XML parsing)
62
+ if "<suggestion>" in suggestion_text and "</suggestion>" in suggestion_text:
63
+ start_idx = suggestion_text.find("<suggestion>") + len("<suggestion>")
64
+ end_idx = suggestion_text.find("</suggestion>")
65
+ analysis = suggestion_text[start_idx:end_idx].strip()
66
+
67
+ if "<example>" in suggestion_text and "</example>" in suggestion_text:
68
+ start_idx = suggestion_text.find("<example>") + len("<example>")
69
+ end_idx = suggestion_text.find("</example>")
70
+ suggestion = suggestion_text[start_idx:end_idx].strip()
71
+
72
+ return {
73
+ "analysis": analysis,
74
+ "suggestion": suggestion
75
+ }
76
+
77
+ @app.route('/', methods=['GET'])
78
+ def index():
79
+ """Serve the main HTML page"""
80
+ return send_file('index.html')
81
+
82
+ @app.route('/api/health', methods=['GET'])
83
+ def health_check():
84
+ """Health check endpoint"""
85
+ return jsonify({"status": "ok"})
86
+
87
+ @app.route('/api/process-image', methods=['POST'])
88
+ def process_image():
89
+ """
90
+ Process a chat screenshot and return analysis and suggestions.
91
+
92
+ Accepts an image file or a base64 encoded image string.
93
+ """
94
+ try:
95
+ # Check if the post request has the file part
96
+ if 'file' in request.files:
97
+ file = request.files['file']
98
+ if file.filename == '':
99
+ return jsonify({"error": "No selected file"}), 400
100
+
101
+ if file and allowed_file(file.filename):
102
+ filename = secure_filename(file.filename)
103
+ filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
104
+ file.save(filepath)
105
+
106
+ # Convert file to base64 for processing
107
+ with open(filepath, "rb") as image_file:
108
+ base64_image = base64.b64encode(image_file.read()).decode('utf-8')
109
+ image_url = f"data:image/{filepath.split('.')[-1]};base64,{base64_image}"
110
+
111
+ # Process the image
112
+ chat_text = img2chat(image_url)
113
+
114
+ # Generate response suggestions
115
+ response_data = generate_response(chat_text)
116
+
117
+ # Return the results
118
+ return jsonify({
119
+ "chat_text": chat_text,
120
+ "analysis": response_data["analysis"],
121
+ "suggestion": response_data["suggestion"]
122
+ })
123
+ else:
124
+ return jsonify({"error": "File type not allowed"}), 400
125
+
126
+ # Check if base64 image was provided
127
+ elif 'image_data' in request.json:
128
+ image_data = request.json['image_data']
129
+
130
+ # Process the image
131
+ chat_text = img2chat(image_data)
132
+
133
+ # Generate response suggestions
134
+ response_data = generate_response(chat_text)
135
+
136
+ # Return the results
137
+ return jsonify({
138
+ "chat_text": chat_text,
139
+ "analysis": response_data["analysis"],
140
+ "suggestion": response_data["suggestion"]
141
+ })
142
+
143
+ else:
144
+ return jsonify({"error": "No image provided"}), 400
145
+
146
+ except Exception as e:
147
+ return jsonify({"error": str(e)}), 500
148
+
149
+ @app.route('/api/process-text', methods=['POST'])
150
+ def process_text():
151
+ """
152
+ Process manually entered chat text and return analysis and suggestions.
153
+ """
154
+ try:
155
+ data = request.json
156
+
157
+ if not data or 'chat_text' not in data:
158
+ return jsonify({"error": "No chat text provided"}), 400
159
+
160
+ chat_text = data['chat_text']
161
+
162
+ # Generate response suggestions
163
+ response_data = generate_response(chat_text)
164
+
165
+ # Return the results
166
+ return jsonify({
167
+ "analysis": response_data["analysis"],
168
+ "suggestion": response_data["suggestion"]
169
+ })
170
+
171
+ except Exception as e:
172
+ return jsonify({"error": str(e)}), 500
173
+
174
+ if __name__ == '__main__':
175
+ port = int(os.environ.get('PORT', 8080))
176
+ app.run(host='0.0.0.0', port=port, debug=True)
chat_analysis.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Chat analysis module for generating response suggestions.
3
+
4
+ This module provides functionality to generate response suggestions
5
+ using OpenAI's GPT models.
6
+ """
7
+
8
+ from openai import OpenAI
9
+
10
+
11
+ def get_suggestion(user_input):
12
+ """
13
+ Generate a response suggestion based on user input.
14
+
15
+ This function uses OpenAI's GPT model to generate a response suggestion
16
+ for a given user input. The response is formatted in XML with suggestion
17
+ and example tags.
18
+
19
+ Args:
20
+ user_input (str): The user's input message
21
+
22
+ Returns:
23
+ str: The generated response suggestion
24
+ """
25
+ client = OpenAI()
26
+
27
+ completion = client.chat.completions.create(
28
+ model="gpt-4o",
29
+ messages=[
30
+ {
31
+ "role": "system",
32
+ "content": "你是一個可以提供尊重對方且讓提問者開心的回覆的助手,"
33
+ "使用者將會輸入對方的問題,回覆時請撇除預設立場且一定要給出回復不要轉移話題,"
34
+ "可以偏愛提問者,要強調出他特殊的魅力。請使用XML格式,"
35
+ "生成針對使用者的應對建議,以及實際舉例來回覆使用者訊息,"
36
+ "使用<suggestion>和<example>標記來格式化你的回覆,<example>不超過30字"
37
+ },
38
+ {"role": "user", "content": user_input}
39
+ ]
40
+ )
41
+
42
+ return completion.choices[0].message.content
43
+
44
+
45
+ if __name__ == "__main__":
46
+ # Example usage
47
+ dialogue = "你喜歡過除了我以外的異性嗎"
48
+ response = get_suggestion(dialogue)
49
+ print(response)
img2chat.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Function to convert an image of chat to structured text using OpenAI's GPT-4o model.
3
+
4
+ This module provides a function to process chat screenshots and convert them
5
+ into structured text format using OpenAI's vision capabilities.
6
+ """
7
+ import os
8
+ import base64
9
+ from dotenv import load_dotenv
10
+ from openai import OpenAI
11
+
12
+ # Load environment variables from .env file
13
+ load_dotenv()
14
+
15
+ def encode_image(image_path):
16
+ """
17
+ Encode an image file to base64.
18
+
19
+ Args:
20
+ image_path (str): Path to the image file
21
+
22
+ Returns:
23
+ str: Base64 encoded image string
24
+ """
25
+ with open(image_path, "rb") as image_file:
26
+ return base64.b64encode(image_file.read()).decode("utf-8")
27
+
28
+ def img2chat(image_input):
29
+ """
30
+ Convert a chat screenshot to structured text using OpenAI's GPT-4o model.
31
+
32
+ This function takes either an image path or a base64 encoded image string
33
+ and uses OpenAI's GPT-4o model to extract the conversation in a structured format.
34
+
35
+ Args:
36
+ image_input (str): Either a file path to an image, a URL, or a base64 encoded image
37
+
38
+ Returns:
39
+ str: The structured chat text extracted from the image
40
+ """
41
+ # Initialize OpenAI client with API key from environment variable
42
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
43
+
44
+ # Determine if the input is a file path, URL, or base64 string
45
+ if os.path.isfile(image_input):
46
+ # It's a file path, encode it to base64
47
+ base64_image = encode_image(image_input)
48
+ image_url = f"data:image/jpeg;base64,{base64_image}"
49
+ elif image_input.startswith("data:image"):
50
+ # It's already a base64 data URL
51
+ image_url = image_input
52
+ elif image_input.startswith("http"):
53
+ # It's a URL
54
+ image_url = image_input
55
+ else:
56
+ # Assume it's a raw base64 string
57
+ image_url = f"data:image/jpeg;base64,{image_input}"
58
+
59
+ response = client.chat.completions.create(
60
+ model="gpt-4o",
61
+ messages=[
62
+ {
63
+ "role": "system",
64
+ "content": "你是一位善於將對話的聊天紀錄的截圖,還原成文本結構的聊天資訊的專家。"
65
+ "對於收到的每一張截圖,請仔細閱讀他們的聊天紀錄,左側的訊息表示其他人傳的訊息、"
66
+ "右邊則是用戶傳的訊息,將聊天紀錄的截圖轉換成文本格式的聊天。"
67
+ "請依照訊息的時間順序,由舊到新的順序,使用XML的格式來輸出聊天紀錄,"
68
+ "<usr>表示使用者的聊天對象的訊息、<self>表示使用者傳的訊息,"
69
+ "最多只能有chat、usr這種二級結構,"
70
+ "例如<chat><usr>你好你好</usr><usr>哈囉哈囉</usr></chat>\n\n"
71
+ "務必要確保忠時的還原使用者的對話紀錄,並且忽略聊天室中像是圖片、音訊等非文字的資訊。"
72
+ "並使用<chat></chat>為標記,包裹整段的聊天資訊。如果你解析不出任何訊息,輸出<chat></chat>就好。"
73
+ },
74
+ {
75
+ "role": "user",
76
+ "content": [
77
+ {"type": "text", "text": "請分析這張聊天截圖並轉換成文本格式:"},
78
+ {
79
+ "type": "image_url",
80
+ "image_url": {
81
+ "url": image_url,
82
+ }
83
+ }
84
+ ]
85
+ }
86
+ ],
87
+ max_tokens=913,
88
+ )
89
+
90
+ return response.choices[0].message.content
91
+
92
+
93
+ if __name__ == "__main__":
94
+ # Example usage with a file path
95
+ print("\nExample with file path:")
96
+ print(img2chat("tst_img/tst1.png"))
index.html ADDED
@@ -0,0 +1,557 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@300;400;700&display=swap" rel="stylesheet">
8
+ <title>聊天助手</title>
9
+ <style>
10
+ * {
11
+ margin: 0;
12
+ padding: 0;
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ body {
17
+ font-family: 'Noto Sans', sans-serif;
18
+ display: flex;
19
+ justify-content: center;
20
+ align-items: center;
21
+ height: 100vh;
22
+ background-color: #f5f5f5;
23
+ }
24
+
25
+ select {
26
+ width: 20%;
27
+ padding: 12px 15px;
28
+ font-size: 18px;
29
+ border: 1px solid #ddd;
30
+ border-radius: 8px;
31
+ background-color: #fafafa;
32
+ color: #333;
33
+ cursor: pointer;
34
+ box-sizing: border-box;
35
+ }
36
+
37
+ select:focus {
38
+ border-color: #007bff;
39
+ outline: none;
40
+ }
41
+
42
+ option {
43
+ padding: 10px;
44
+ }
45
+
46
+ .chatbox {
47
+ height: fit-content;
48
+ width: 80%;
49
+ background-color: white;
50
+ border-radius: 20px;
51
+ margin-top: 10px;
52
+ margin-bottom: 10px;
53
+ }
54
+
55
+ .myInputArea {
56
+ display: flex;
57
+ justify-content: center;
58
+ margin-top: 10px;
59
+ }
60
+
61
+ .myMessageArea {
62
+ display: flex;
63
+ justify-content: flex-end;
64
+ margin-top: 10px;
65
+ }
66
+
67
+ .GPTmessageArea {
68
+ display: flex;
69
+ justify-content: flex-start;
70
+ margin-top: 10px;
71
+ }
72
+
73
+ .myMessageBox {
74
+ height: fit-content;
75
+ width: fit-content;
76
+ margin: 10px;
77
+ padding: 10px;
78
+ border-radius: 10px;
79
+ background-color: rgb(195, 252, 233);
80
+ }
81
+
82
+ .GPTmessageBox {
83
+ height: fit-content;
84
+ width: fit-content;
85
+ margin: 10px;
86
+ padding: 10px;
87
+ border-radius: 10px;
88
+ background-color: rgb(252, 206, 238);
89
+ }
90
+
91
+ .myInputBox {
92
+ height: fit-content;
93
+ width: 70%;
94
+ margin: 10px;
95
+ padding: 10px;
96
+ border-radius: 10px;
97
+ background-color: #f5f5f5;
98
+ }
99
+
100
+ .addMessageButton {
101
+ width: 35px;
102
+ height: 35px;
103
+ font-size: 15px;
104
+ font-weight: bold;
105
+ border: solid 1px #a7a7a7;
106
+ border-radius: 10px;
107
+ background-color: white;
108
+ cursor: pointer;
109
+ }
110
+
111
+ .sendMessageButton {
112
+ background-color: #007bff;
113
+ /* 藍色 */
114
+ color: white;
115
+ font-size: 16px;
116
+ padding: 10px 20px;
117
+ border: none;
118
+ border-radius: 5px;
119
+ cursor: pointer;
120
+ transition: background 0.3s;
121
+ }
122
+
123
+ .sendMessageButton:hover {
124
+ background-color: #0056b3;
125
+ }
126
+
127
+ .addMessageButton:hover {
128
+ background-color: rgb(209, 209, 209);
129
+ }
130
+
131
+ .messagePending {
132
+ margin-left: 5px;
133
+ width: 70%;
134
+ padding: 12px 15px;
135
+ font-size: 16px;
136
+ border: 2px solid #ccc;
137
+ border-radius: 8px;
138
+ background-color: #fafafa;
139
+ color: #333;
140
+ outline: none;
141
+ box-sizing: border-box;
142
+ transition: border 0.3s ease, box-shadow 0.3s ease;
143
+ }
144
+
145
+ .messagePending:focus {
146
+ border-color: #007bff;
147
+ box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
148
+ }
149
+
150
+ .messagePending::placeholder {
151
+ color: #888;
152
+ font-style: italic;
153
+ }
154
+
155
+ #imgInputBox {
156
+ width: 100%;
157
+ border: solid 1px #a7a7a7;
158
+ border-radius: 10px;
159
+ height: 50px;
160
+ }
161
+
162
+ #textInputBox {
163
+ margin-top: 10px;
164
+ width: 100%;
165
+ border: solid 1px #a7a7a7;
166
+ border-radius: 10px;
167
+ height: 200px;
168
+ padding: 10px;
169
+ overflow: auto;
170
+ }
171
+ </style>
172
+ </head>
173
+
174
+ <body>
175
+ <div id="chatbox" class="chatbox">
176
+
177
+ <!--對話輸入框-->
178
+ <div class="myInputArea">
179
+ <div class="myInputBox">
180
+ <div id="imgInputBox">
181
+ <div style="display: flex; justify-content: center; align-items: center; height: 100%;">
182
+ <input type="file" id="imageUpload" accept="image/*" style="display: none;">
183
+ <button onclick="document.getElementById('imageUpload').click()" style="padding: 5px 10px; background-color: #f0f0f0; border: 1px solid #ccc; border-radius: 5px; cursor: pointer;">選擇圖片</button>
184
+ <span id="selectedFileName" style="margin-left: 10px; font-size: 14px;"></span>
185
+ </div>
186
+ </div>
187
+ <div style="text-align: center;">
188
+
189
+ </div>
190
+ <div id="textInputBox">
191
+ <!--已建立的訊息填寫框-->
192
+ <div style="display: flex;flex-wrap: nowrap;justify-content: end; margin-top: 10px;">
193
+ <select id="character0" name="character">
194
+ <option value="self">自己</option>
195
+ <option value="usr">對方</option>
196
+ </select>
197
+ <input type="text" id="messagePending0" name="messagePending" class="messagePending">
198
+ </div>
199
+
200
+ <!--建立新的訊息填寫框-->
201
+ <div style="display: flex;justify-content: end; margin-top: 10px;">
202
+ <button class="addMessageButton" onclick="addMessageBox()">+</button>
203
+ </div>
204
+ </div>
205
+ <!--傳送訊息-->
206
+ <div style="display: flex;justify-content: end;margin-top: 10px;">
207
+ <button class="sendMessageButton" onclick="sendMessage()">傳送</button>
208
+ </div>
209
+ </div>
210
+ </div>
211
+
212
+ <!--對話紀錄-我方-->
213
+ <!--
214
+ <div class="myMessageArea">
215
+ <div class="myMessageBox">
216
+ <p>這是我傳的訊息或圖片</p>
217
+ </div>
218
+ </div>
219
+
220
+
221
+ <div class="GPTmessageArea">
222
+ <div class="GPTmessageBox">
223
+ <p>這是GPT傳的訊息</p>
224
+ </div>
225
+ </div>
226
+ -->
227
+ </div>
228
+
229
+ <script>
230
+ let messageCount = 1; // 記錄訊息框數量
231
+ let selectedImage = null; // 儲存選擇的圖片
232
+
233
+ // 當頁面載入完成後,添加事件監聽器
234
+ document.addEventListener('DOMContentLoaded', function() {
235
+ // 為圖片上傳添加事件監聽器
236
+ const imageUpload = document.getElementById('imageUpload');
237
+ if (imageUpload) {
238
+ imageUpload.addEventListener('change', handleImageSelect);
239
+ }
240
+ });
241
+
242
+ // 處理圖片選擇
243
+ function handleImageSelect(event) {
244
+ const file = event.target.files[0];
245
+ if (file) {
246
+ selectedImage = file;
247
+ document.getElementById('selectedFileName').textContent = file.name;
248
+
249
+ // 選擇圖片後立即上傳,不需要額外的上傳按鈕
250
+ uploadImage();
251
+ }
252
+ }
253
+
254
+ // 上傳圖片到伺服器
255
+ function uploadImage() {
256
+ if (!selectedImage) {
257
+ alert('請先選擇一張圖片');
258
+ return;
259
+ }
260
+
261
+ const formData = new FormData();
262
+ formData.append('file', selectedImage);
263
+
264
+ // 暫時刪除 myInputArea
265
+ const myInputArea = document.querySelector(".myInputArea");
266
+ if (myInputArea) {
267
+ myInputArea.remove();
268
+ }
269
+
270
+ // 新增 myMessageArea (顯示上傳的圖片)
271
+ const chatbox = document.getElementById("chatbox");
272
+ const myMessageArea = document.createElement("div");
273
+ myMessageArea.className = "myMessageArea";
274
+ myMessageArea.innerHTML = `
275
+ <div class="myMessageBox">
276
+ <p>上傳的圖片: ${selectedImage.name}</p>
277
+ </div>
278
+ `;
279
+ chatbox.appendChild(myMessageArea);
280
+
281
+ // 新增 GPTmessageArea (初始內容: 等待回應...)
282
+ const GPTmessageArea = document.createElement("div");
283
+ GPTmessageArea.className = "GPTmessageArea";
284
+ GPTmessageArea.innerHTML = `
285
+ <div class="GPTmessageBox">
286
+ <p>(等待回應......)</p>
287
+ </div>
288
+ `;
289
+ chatbox.appendChild(GPTmessageArea);
290
+
291
+ // 發送 API 請求
292
+ fetch('/api/process-image', {
293
+ method: 'POST',
294
+ body: formData
295
+ })
296
+ .then(response => {
297
+ if (!response.ok) {
298
+ throw new Error(`HTTP error! Status: ${response.status}`);
299
+ }
300
+ return response.json();
301
+ })
302
+ .then(data => {
303
+ console.log("Server Response:", data);
304
+
305
+ // 確保回應包含 analysis 和 suggestion
306
+ const analysis = data.analysis || "無分析";
307
+ const suggestion = data.suggestion || "無建議";
308
+ const chatText = data.chat_text || "";
309
+
310
+ // 處理 chatText,去掉外層的 <chat> 標記
311
+ let formattedChatText = chatText;
312
+ if (formattedChatText.startsWith("<chat>") && formattedChatText.endsWith("</chat>")) {
313
+ formattedChatText = formattedChatText.substring(6, formattedChatText.length - 7);
314
+ }
315
+
316
+ // 先顯示識別的聊天內容
317
+ GPTmessageArea.innerHTML = `
318
+ <div class="GPTmessageBox">
319
+ <p><strong>識別的聊天內容:</strong><br>${formattedChatText}</p>
320
+ </div>
321
+ `;
322
+
323
+ // 創建新的 GPTmessageArea 來顯示分析和建議
324
+ const analysisArea = document.createElement("div");
325
+ analysisArea.className = "GPTmessageArea";
326
+ analysisArea.innerHTML = `
327
+ <div class="GPTmessageBox">
328
+ <p><strong>分析:</strong><br>${analysis}</p>
329
+ <p><strong>建議回覆:</strong><br>${suggestion}</p>
330
+ </div>
331
+ `;
332
+ chatbox.appendChild(analysisArea);
333
+ })
334
+ .catch(error => {
335
+ console.error("Error uploading image:", error);
336
+ GPTmessageArea.innerHTML = `
337
+ <div class="GPTmessageBox">
338
+ <p>(錯誤:無法取得回應)</p>
339
+ <p>錯誤詳情:${error.message}</p>
340
+ </div>
341
+ `;
342
+ })
343
+ .finally(() => {
344
+ // 重新新增 myInputArea
345
+ resetInputArea();
346
+ });
347
+ }
348
+
349
+ // 重設輸入區域
350
+ function resetInputArea() {
351
+ const chatbox = document.getElementById("chatbox");
352
+ const newMyInputArea = document.createElement("div");
353
+ newMyInputArea.className = "myInputArea";
354
+ newMyInputArea.innerHTML = `
355
+ <div class="myInputBox">
356
+ <div id="imgInputBox">
357
+ <div style="display: flex; justify-content: center; align-items: center; height: 100%;">
358
+ <input type="file" id="imageUpload" accept="image/*" style="display: none;">
359
+ <button onclick="document.getElementById('imageUpload').click()" style="padding: 5px 10px; background-color: #f0f0f0; border: 1px solid #ccc; border-radius: 5px; cursor: pointer;">選擇圖片</button>
360
+ <span id="selectedFileName" style="margin-left: 10px; font-size: 14px;"></span>
361
+ </div>
362
+ </div>
363
+ <div style="text-align: center;">
364
+
365
+ </div>
366
+ <div id="textInputBox">
367
+ <!--已建立的訊息填寫框-->
368
+ <div style="display: flex;flex-wrap: nowrap;justify-content: end; margin-top: 10px;">
369
+ <select id="character0" name="character">
370
+ <option value="self">自己</option>
371
+ <option value="usr">對方</option>
372
+ </select>
373
+ <input type="text" id="messagePending0" name="messagePending" class="messagePending">
374
+ </div>
375
+
376
+ <!--建立新的訊息填寫框-->
377
+ <div style="display: flex;justify-content: end; margin-top: 10px;">
378
+ <button class="addMessageButton" onclick="addMessageBox()">+</button>
379
+ </div>
380
+ </div>
381
+ <!--傳送訊息-->
382
+ <div style="display: flex;justify-content: end;margin-top: 10px;">
383
+ <button class="sendMessageButton" onclick="sendMessage()">傳送</button>
384
+ </div>
385
+ </div>
386
+ `;
387
+ chatbox.appendChild(newMyInputArea);
388
+
389
+ // 重新添加事件監聽器
390
+ const imageUpload = document.getElementById('imageUpload');
391
+ if (imageUpload) {
392
+ imageUpload.addEventListener('change', handleImageSelect);
393
+ }
394
+
395
+ // 重設選擇的圖片
396
+ selectedImage = null;
397
+ messageCount = 1;
398
+ }
399
+
400
+ // 添加新的訊息填寫框
401
+ function addMessageBox() {
402
+ const newMessageBox = document.createElement('div');
403
+ newMessageBox.style.display = "flex";
404
+ newMessageBox.style.justifyContent = "end";
405
+ newMessageBox.style.marginTop = "10px";
406
+
407
+ // 新增一個X按鈕
408
+ const deleteButton = document.createElement('button');
409
+ deleteButton.innerText = 'X';
410
+ deleteButton.style.marginRight = '10px';
411
+ deleteButton.style.cursor = 'pointer';
412
+ deleteButton.style.background = 'red';
413
+ deleteButton.style.color = 'white';
414
+ deleteButton.style.border = 'none';
415
+ deleteButton.style.borderRadius = '50%';
416
+ deleteButton.style.width = '25px';
417
+ deleteButton.style.height = '25px';
418
+ deleteButton.style.textAlign = 'center';
419
+ deleteButton.style.fontSize = '16px';
420
+
421
+ // 點擊刪除按鈕時刪除對應的訊息框
422
+ deleteButton.addEventListener('click', function() {
423
+ newMessageBox.remove();
424
+ });
425
+
426
+ // 設定新增訊息框的內容
427
+ newMessageBox.innerHTML = `
428
+ <select id="character${messageCount}" name="character">
429
+ <option value="self">自己</option>
430
+ <option value="usr">對方</option>
431
+ </select>
432
+ <input type="text" id="messagePending${messageCount}" name="messagePending" class="messagePending">
433
+ `;
434
+
435
+ // 在訊息框前插入X按鈕
436
+ newMessageBox.insertBefore(deleteButton, newMessageBox.firstChild);
437
+
438
+ // 把新的訊息框插入到#textInputBox
439
+ document.getElementById('textInputBox').insertBefore(newMessageBox, document.querySelector('.addMessageButton').parentNode);
440
+ messageCount++;
441
+ }
442
+
443
+ function sendMessage() {
444
+ let result = "<chat>";
445
+ let formattedMessage = ""; // 用於顯示在 myMessageArea 的格式化文字
446
+
447
+ for (let i = 0; i < messageCount; i++) {
448
+ try {
449
+ const character = document.getElementById(`character${i}`);
450
+ const messageElement = document.getElementById(`messagePending${i}`);
451
+ if (!character || !messageElement) continue; // 跳過已刪除的元素
452
+
453
+ const message = messageElement.value.trim();
454
+ if (message) {
455
+ result += `<${character.value}>${message}</${character.value}>`;
456
+ formattedMessage += `${character.value === 'self' ? "您" : "對方"}: ${message}<br>`; // **換行**
457
+ }
458
+ } catch (error) {
459
+ console.warn(`Message box ${i} does not exist. Skipping.`);
460
+ continue;
461
+ }
462
+ }
463
+ result += "</chat>";
464
+
465
+ // **1. 暫時刪除 myInputArea**
466
+ const myInputArea = document.querySelector(".myInputArea");
467
+ if (myInputArea) {
468
+ myInputArea.remove();
469
+ }
470
+
471
+ // **2. 新增 myMessageArea (符合提供的 HTML 結構)**
472
+ const chatbox = document.getElementById("chatbox");
473
+ const myMessageArea = document.createElement("div");
474
+ myMessageArea.className = "myMessageArea";
475
+ myMessageArea.innerHTML = `
476
+ <div class="myMessageBox">
477
+ <p>${formattedMessage}</p>
478
+ </div>
479
+ `;
480
+ chatbox.appendChild(myMessageArea);
481
+
482
+ // **3. 新增 GPTmessageArea (初始內容: 等待回應...)**
483
+ const GPTmessageArea = document.createElement("div");
484
+ GPTmessageArea.className = "GPTmessageArea";
485
+ GPTmessageArea.innerHTML = `
486
+ <div class="GPTmessageBox">
487
+ <p>(等待回應......)</p>
488
+ </div>
489
+ `;
490
+ chatbox.appendChild(GPTmessageArea);
491
+
492
+ // **4. 傳送 API 請求**
493
+ const data = {
494
+ chat_text: result
495
+ };
496
+ fetch('/api/process-text', {
497
+ method: 'POST',
498
+ headers: {
499
+ 'Content-Type': 'application/json'
500
+ },
501
+ body: JSON.stringify(data)
502
+ })
503
+ .then(response => {
504
+ if (!response.ok) {
505
+ throw new Error(`HTTP error! Status: ${response.status}`);
506
+ }
507
+ return response.json();
508
+ })
509
+ .then(data => {
510
+ console.log("Server Response:", data);
511
+
512
+ // 確保回應包含 analysis 和 suggestion
513
+ const analysis = data.analysis || "無分析";
514
+ const suggestion = data.suggestion || "無建議";
515
+
516
+ // 處理 result,去掉外層的 <chat> 標記
517
+ let formattedChatText = result;
518
+ if (formattedChatText.startsWith("<chat>") && formattedChatText.endsWith("</chat>")) {
519
+ formattedChatText = formattedChatText.substring(6, formattedChatText.length - 7);
520
+ }
521
+
522
+ // 先顯示原始聊天文本
523
+ GPTmessageArea.innerHTML = `
524
+ <div class="GPTmessageBox">
525
+ <p><strong>識別的聊天內容:</strong><br>${formattedChatText}</p>
526
+ </div>
527
+ `;
528
+
529
+ // 創建新的 GPTmessageArea 來顯示分析和建議
530
+ const analysisArea = document.createElement("div");
531
+ analysisArea.className = "GPTmessageArea";
532
+ analysisArea.innerHTML = `
533
+ <div class="GPTmessageBox">
534
+ <p><strong>分析:</strong><br>${analysis}</p>
535
+ <p><strong>建議回覆:</strong><br>${suggestion}</p>
536
+ </div>
537
+ `;
538
+ chatbox.appendChild(analysisArea);
539
+ })
540
+ .catch(error => {
541
+ console.error("Error sending message:", error);
542
+ GPTmessageArea.innerHTML = `
543
+ <div class="GPTmessageBox">
544
+ <p>(錯誤:無法取得回應)</p>
545
+ <p>錯誤詳情:${error.message}</p>
546
+ </div>
547
+ `;
548
+ })
549
+ .finally(() => {
550
+ // 使用重設輸入區域函數
551
+ resetInputArea();
552
+ });
553
+ }
554
+ </script>
555
+ </body>
556
+
557
+ </html>
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ flask==2.3.3
2
+ flask-cors==4.0.0
3
+ python-dotenv==1.0.0
4
+ openai==1.3.0
5
+ werkzeug==2.3.7
6
+ gunicorn==21.2.0