@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanskong/office-assistant",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Office Assistant - AI-powered Office automation via MCP protocol with license protection",
5
5
  "main": "bin/office-assistant.js",
6
6
  "bin": {
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
- self._default_folder = "D:\\@OfficeMCP"
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
- if asNewInstance:
147
- app = win32com.client.Dispatch(app_full_name)
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
- self.__dict__[app_name_attr] = app
156
- return app
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
- if subfolder is None:
229
- file_path = os.path.join(self.RootFolder, file_name)
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
- file_path = os.path.join(self.RootFolder, subfolder, file_name)
232
- return file_path
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
- if app_name == 'Excel':
105
- doc = app.Workbooks.Open(file_path)
106
- elif app_name == 'Word':
107
- doc = app.Documents.Open(file_path)
108
- elif app_name == 'PowerPoint':
109
- doc = app.Presentations.Open(file_path)
110
-
111
- app.Visible = True
112
- return {"success": True, "app": app_name, "file": file_path}
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."""