mistpe commited on
Commit
89bf662
1 Parent(s): bc83caa

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +241 -0
app.py ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ from flask import Flask, request, make_response
4
+ import hashlib
5
+ import time
6
+ import xml.etree.ElementTree as ET
7
+ import os
8
+ import json
9
+ from openai import OpenAI
10
+ from dotenv import load_dotenv
11
+ from duckduckgo_search import DDGS
12
+ import smtplib
13
+ from email.mime.text import MIMEText
14
+ from email.mime.multipart import MIMEMultipart
15
+
16
+ # 加载环境变量
17
+ load_dotenv()
18
+
19
+ app = Flask(__name__)
20
+
21
+ # 配置
22
+ TOKEN = os.getenv('TOKEN')
23
+ API_KEY = os.getenv("API_KEY")
24
+ BASE_URL = os.getenv("OPENAI_BASE_URL")
25
+ EMAIL_KEY = os.getenv("EMAIL_KEY")
26
+ client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
27
+
28
+ # 存储用户会话信息
29
+ user_sessions = {}
30
+
31
+ # 定义函数列表
32
+ FUNCTIONS = [
33
+ {
34
+ "name": "search_duckduckgo",
35
+ "description": "使用DuckDuckGo搜索引擎查询信息。可以搜索最新新闻、文章、博客等内容。",
36
+ "parameters": {
37
+ "type": "object",
38
+ "properties": {
39
+ "keywords": {
40
+ "type": "array",
41
+ "items": {"type": "string"},
42
+ "description": "搜索的关键词列表。例如:['Python', '机器学习', '最新进展']。"
43
+ }
44
+ },
45
+ "required": ["keywords"]
46
+ }
47
+ },
48
+ {
49
+ "name": "send_email",
50
+ "description": "发送电子邮件。",
51
+ "parameters": {
52
+ "type": "object",
53
+ "properties": {
54
+ "to": {
55
+ "type": "string",
56
+ "description": "收件人邮箱地址"
57
+ },
58
+ "subject": {
59
+ "type": "string",
60
+ "description": "邮件主题"
61
+ },
62
+ "content": {
63
+ "type": "string",
64
+ "description": "邮件内容"
65
+ }
66
+ },
67
+ "required": ["to", "subject", "content"]
68
+ }
69
+ }
70
+ ]
71
+
72
+ def verify_wechat(request):
73
+ # 获取微信服务器发送过来的参数
74
+ data = request.args
75
+ signature = data.get('signature')
76
+ timestamp = data.get('timestamp')
77
+ nonce = data.get('nonce')
78
+ echostr = data.get('echostr')
79
+
80
+ # 对参数进行字典排序,拼接字符串
81
+ temp = [timestamp, nonce, TOKEN]
82
+ temp.sort()
83
+ temp = ''.join(temp)
84
+
85
+ # 加密
86
+ if (hashlib.sha1(temp.encode('utf8')).hexdigest() == signature):
87
+ return echostr
88
+ else:
89
+ return 'error', 403
90
+
91
+ def getUserMessageContentFromXML(xml_content):
92
+ # 解析XML字符串
93
+ root = ET.fromstring(xml_content)
94
+ # 提取数据
95
+ content = root.find('Content').text
96
+ from_user_name = root.find('FromUserName').text
97
+ to_user_name = root.find('ToUserName').text
98
+ return content, from_user_name, to_user_name
99
+
100
+ def generate_response_xml(from_user_name, to_user_name, output_content):
101
+ output_xml = '''
102
+ <xml>
103
+ <ToUserName><![CDATA[%s]]></ToUserName>
104
+ <FromUserName><![CDATA[%s]]></FromUserName>
105
+ <CreateTime>%s</CreateTime>
106
+ <MsgType><![CDATA[text]]></MsgType>
107
+ <Content><![CDATA[%s]]></Content>
108
+ </xml>'''
109
+
110
+ response = make_response(output_xml % (from_user_name, to_user_name, str(int(time.time())), output_content))
111
+ response.content_type = 'application/xml'
112
+ return response
113
+
114
+ def get_openai_response(messages, functions=None, function_call=None):
115
+ try:
116
+ response = client.chat.completions.create(
117
+ model="gpt-4o-mini",
118
+ messages=messages,
119
+ functions=functions,
120
+ function_call=function_call
121
+ )
122
+ return response.choices[0].message
123
+ except Exception as e:
124
+ print(f"调用OpenAI API时出错: {str(e)}")
125
+ return None
126
+
127
+ def split_message(message, max_length=500):
128
+ return [message[i:i+max_length] for i in range(0, len(message), max_length)]
129
+
130
+ def search_duckduckgo(keywords):
131
+ search_term = " ".join(keywords)
132
+ with DDGS() as ddgs:
133
+ return list(ddgs.text(keywords=search_term, region="cn-zh", safesearch="on", max_results=5))
134
+
135
+ def send_email(to, subject, content):
136
+ try:
137
+ with smtplib.SMTP('106.15.184.28', 8025) as smtp:
138
+ smtp.login("jwt", EMAIL_KEY)
139
+ message = MIMEMultipart()
140
+ message['From'] = "Me <[email protected]>"
141
+ message['To'] = to
142
+ message['Subject'] = subject
143
+ message.attach(MIMEText(content, 'html'))
144
+ smtp.sendmail("[email protected]", to, message.as_string())
145
+ return True
146
+ except Exception as e:
147
+ print(f"发送邮件时出错: {str(e)}")
148
+ return False
149
+
150
+ def process_function_call(response_message, session):
151
+ function_name = response_message.function_call.name
152
+ function_args = json.loads(response_message.function_call.arguments)
153
+ print(f"\n模型选择调用函数: {function_name}")
154
+
155
+ if function_name == "search_duckduckgo":
156
+ keywords = function_args.get('keywords', [])
157
+ if not keywords:
158
+ print("错误:模型没有提供搜索关键词")
159
+ return None
160
+ print(f"关键词: {', '.join(keywords)}")
161
+ return search_duckduckgo(keywords)
162
+ elif function_name == "send_email":
163
+ to = function_args.get('to')
164
+ subject = function_args.get('subject')
165
+ content = function_args.get('content')
166
+ if not session.get('email_sent', False):
167
+ if send_email(to, subject, content):
168
+ session['email_sent'] = True
169
+ return "邮件发送成功"
170
+ else:
171
+ return "邮件发送失败"
172
+ else:
173
+ return "邮件已经发送过,不再重复发送。"
174
+ else:
175
+ print(f"未知的函数名称: {function_name}")
176
+ return None
177
+
178
+ @app.route('/api/wx', methods=['GET', 'POST'])
179
+ def wechatai():
180
+ if request.method == 'GET':
181
+ return verify_wechat(request)
182
+ else:
183
+ # 处理POST请求
184
+ print("user request data: ", request.data)
185
+ user_message_content, from_user_name, to_user_name = getUserMessageContentFromXML(request.data)
186
+ print("user message content: ", user_message_content)
187
+
188
+ if from_user_name not in user_sessions:
189
+ user_sessions[from_user_name] = {'messages': [], 'pending_response': [], 'email_sent': False}
190
+
191
+ session = user_sessions[from_user_name]
192
+
193
+ if user_message_content.lower() == '继续':
194
+ if session['pending_response']:
195
+ response_content = session['pending_response'].pop(0)
196
+ if session['pending_response']:
197
+ response_content += '\n\n回复"继续"获取下一部分。'
198
+ else:
199
+ response_content += '\n\n回复结束。'
200
+ else:
201
+ response_content = "没有待发送的消息。"
202
+ else:
203
+ session['messages'].append({"role": "user", "content": user_message_content})
204
+
205
+ response_message = get_openai_response(session['messages'], functions=FUNCTIONS, function_call="auto")
206
+
207
+ if response_message.function_call:
208
+ function_response = process_function_call(response_message, session)
209
+ if function_response:
210
+ session['messages'].extend([
211
+ response_message.model_dump(),
212
+ {
213
+ "role": "function",
214
+ "name": response_message.function_call.name,
215
+ "content": json.dumps(function_response, ensure_ascii=False)
216
+ }
217
+ ])
218
+ final_response = get_openai_response(session['messages'])
219
+ if final_response:
220
+ gpt_response = final_response.content
221
+ else:
222
+ gpt_response = "抱歉,我遇到了一些问题,无法回答您的问题。"
223
+ else:
224
+ gpt_response = "抱歉,我在执行任务时遇到了问题。"
225
+ else:
226
+ gpt_response = response_message.content
227
+
228
+ session['messages'].append({"role": "assistant", "content": gpt_response})
229
+
230
+ response_parts = split_message(gpt_response)
231
+
232
+ if len(response_parts) > 1:
233
+ response_content = response_parts[0] + '\n\n回复"继续"获取下一部分。'
234
+ session['pending_response'] = response_parts[1:]
235
+ else:
236
+ response_content = response_parts[0]
237
+
238
+ return generate_response_xml(from_user_name, to_user_name, response_content)
239
+
240
+ if __name__ == '__main__':
241
+ app.run(host='0.0.0.0', port=7860, debug=True)