@tanskong/office-assistant 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/officer.py +37 -28
- package/src/server.py +27 -15
package/package.json
CHANGED
package/src/officer.py
CHANGED
|
@@ -3,11 +3,15 @@ import pywintypes
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
5
|
import win32com.client
|
|
6
|
+
import pythoncom
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class TheOfficer:
|
|
10
11
|
def __init__(self):
|
|
12
|
+
# 初始化 COM
|
|
13
|
+
pythoncom.CoInitialize()
|
|
14
|
+
|
|
11
15
|
self._excel = None
|
|
12
16
|
self._word = None
|
|
13
17
|
self._outlook = None
|
|
@@ -21,9 +25,9 @@ class TheOfficer:
|
|
|
21
25
|
self._kwpp = None
|
|
22
26
|
self._kwps = None
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
# 使用用户目录,避免硬编码 D 盘
|
|
29
|
+
self._default_folder = os.path.join(os.path.expanduser("~"), "OfficeMCP_Data")
|
|
25
30
|
self.Version = "1.0.5"
|
|
26
|
-
self.Speaker = None
|
|
27
31
|
self.ComObjects = {}
|
|
28
32
|
self._printable = False
|
|
29
33
|
self.Data = OfficerData()
|
|
@@ -50,18 +54,6 @@ class TheOfficer:
|
|
|
50
54
|
self.ComObjects[com_name] = comobject
|
|
51
55
|
return comobject
|
|
52
56
|
|
|
53
|
-
def Speak(self, text: str = "Hello!, I'm Office Assistant.", volume: int = 80, rate: int = 2) -> bool:
|
|
54
|
-
try:
|
|
55
|
-
if self.Speaker is None:
|
|
56
|
-
self.Speaker = self.GetComObject("SAPI.SPVOICE")
|
|
57
|
-
self.Speaker.Volume = volume
|
|
58
|
-
self.Speaker.Rate = rate
|
|
59
|
-
self.Speaker.Speak(text)
|
|
60
|
-
return True
|
|
61
|
-
except Exception as e:
|
|
62
|
-
print(e)
|
|
63
|
-
return False
|
|
64
|
-
|
|
65
57
|
def DownloadImage(self, url: str, save_path: str = None) -> str:
|
|
66
58
|
import datetime
|
|
67
59
|
if save_path is None:
|
|
@@ -130,6 +122,12 @@ class TheOfficer:
|
|
|
130
122
|
return False
|
|
131
123
|
|
|
132
124
|
def Application(self, app_name: str, asNewInstance: bool = False):
|
|
125
|
+
# 确保 COM 已初始化
|
|
126
|
+
try:
|
|
127
|
+
pythoncom.CoInitialize()
|
|
128
|
+
except:
|
|
129
|
+
pass
|
|
130
|
+
|
|
133
131
|
app_name_attr = "_" + app_name.lower()
|
|
134
132
|
if hasattr(self, app_name_attr):
|
|
135
133
|
app = getattr(self, app_name_attr)
|
|
@@ -143,17 +141,21 @@ class TheOfficer:
|
|
|
143
141
|
if not self.IsAppAvailable(app_name):
|
|
144
142
|
return None
|
|
145
143
|
app_full_name = app_name + ".Application"
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
self.__dict__[app_name_attr] = app
|
|
149
|
-
return app
|
|
150
|
-
else:
|
|
151
|
-
try:
|
|
152
|
-
app = win32com.client.GetActiveObject(app_full_name)
|
|
153
|
-
except pywintypes.com_error:
|
|
144
|
+
try:
|
|
145
|
+
if asNewInstance:
|
|
154
146
|
app = win32com.client.Dispatch(app_full_name)
|
|
155
|
-
|
|
156
|
-
|
|
147
|
+
self.__dict__[app_name_attr] = app
|
|
148
|
+
return app
|
|
149
|
+
else:
|
|
150
|
+
try:
|
|
151
|
+
app = win32com.client.GetActiveObject(app_full_name)
|
|
152
|
+
except pywintypes.com_error:
|
|
153
|
+
app = win32com.client.Dispatch(app_full_name)
|
|
154
|
+
self.__dict__[app_name_attr] = app
|
|
155
|
+
return app
|
|
156
|
+
except Exception as e:
|
|
157
|
+
print(f"[ERROR] Failed to create {app_name} application: {e}")
|
|
158
|
+
return None
|
|
157
159
|
|
|
158
160
|
def RunningApps(self) -> list:
|
|
159
161
|
apps = []
|
|
@@ -225,11 +227,18 @@ class TheOfficer:
|
|
|
225
227
|
return True
|
|
226
228
|
|
|
227
229
|
def FilePath(self, file_name: str = None, subfolder: str = None) -> str:
|
|
228
|
-
|
|
229
|
-
|
|
230
|
+
# 优先使用智能办公区路径
|
|
231
|
+
workspace = os.path.join(os.path.expanduser("~"), "Desktop", "智能办公区")
|
|
232
|
+
if not os.path.exists(workspace):
|
|
233
|
+
workspace = self.RootFolder
|
|
234
|
+
|
|
235
|
+
if subfolder:
|
|
236
|
+
folder = os.path.join(workspace, subfolder)
|
|
230
237
|
else:
|
|
231
|
-
|
|
232
|
-
|
|
238
|
+
folder = workspace
|
|
239
|
+
|
|
240
|
+
os.makedirs(folder, exist_ok=True)
|
|
241
|
+
return os.path.join(folder, file_name)
|
|
233
242
|
|
|
234
243
|
def ScreenShot(self, save_path: str = None) -> str:
|
|
235
244
|
import win32gui
|
package/src/server.py
CHANGED
|
@@ -96,20 +96,38 @@ def open_file(file_path: str) -> dict:
|
|
|
96
96
|
- PowerPoint: .pptx, .ppt
|
|
97
97
|
|
|
98
98
|
Tip: Place files in "Desktop/智能办公区/数据" for safe processing.
|
|
99
|
+
Note: For Chinese file paths, use run_python tool instead.
|
|
99
100
|
"""
|
|
101
|
+
import os
|
|
102
|
+
|
|
103
|
+
# 处理中文路径编码
|
|
104
|
+
# 方法1: 使用绝对路径
|
|
105
|
+
file_path = os.path.abspath(file_path)
|
|
106
|
+
|
|
107
|
+
# 方法2: 确保路径存在
|
|
108
|
+
if not os.path.exists(file_path):
|
|
109
|
+
return {"success": False, "error": f"File not found: {file_path}"}
|
|
110
|
+
|
|
100
111
|
ext = file_path.split('.')[-1].lower()
|
|
101
112
|
app_name = APP_MAP.get(ext, 'Excel')
|
|
102
113
|
app = Officer.Application(app_name)
|
|
114
|
+
|
|
115
|
+
if app is None:
|
|
116
|
+
return {"success": False, "error": f"Failed to start {app_name}"}
|
|
103
117
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
118
|
+
try:
|
|
119
|
+
if app_name == 'Excel':
|
|
120
|
+
# 使用 Unicode 字符串传递路径
|
|
121
|
+
doc = app.Workbooks.Open(file_path)
|
|
122
|
+
elif app_name == 'Word':
|
|
123
|
+
doc = app.Documents.Open(file_path)
|
|
124
|
+
elif app_name == 'PowerPoint':
|
|
125
|
+
doc = app.Presentations.Open(file_path)
|
|
126
|
+
|
|
127
|
+
app.Visible = True
|
|
128
|
+
return {"success": True, "app": app_name, "file": file_path}
|
|
129
|
+
except Exception as e:
|
|
130
|
+
return {"success": False, "error": str(e), "note": "For Chinese paths, use run_python tool"}
|
|
113
131
|
|
|
114
132
|
|
|
115
133
|
@mcp.tool()
|
|
@@ -188,12 +206,6 @@ def download(url: str, save_name: str = None) -> str:
|
|
|
188
206
|
return Officer.DownloadURL(url, save_name)
|
|
189
207
|
|
|
190
208
|
|
|
191
|
-
@mcp.tool()
|
|
192
|
-
def speak(text: str = "Hello", volume: int = 80, rate: int = 0) -> bool:
|
|
193
|
-
"""Text-to-speech. Volume: 0-100, Rate: -10 to 10."""
|
|
194
|
-
return Officer.Speak(text, volume, rate)
|
|
195
|
-
|
|
196
|
-
|
|
197
209
|
@mcp.tool()
|
|
198
210
|
def demonstrate() -> dict:
|
|
199
211
|
"""Run a demonstration of Office automation capabilities."""
|