soulsync 1.0.12 → 1.0.14
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/SSL_FIX_ANALYSIS.md +84 -0
- package/SSL_FIX_REPORT.md +90 -0
- package/config.json.example +2 -8
- package/index.js +493 -52
- package/openclaw.plugin.json +4 -4
- package/package.json +4 -1
- package/src/client.py +23 -38
- package/src/config.js +100 -0
- package/src/device-code.js +150 -0
- package/src/main.py +21 -49
- package/src/oauth-server.js +48 -0
- package/src/profiles.py +27 -49
- package/src/sync.py +67 -60
- package/test_ssl_fix.py +58 -0
- package/src/interactive_auth.py +0 -283
- package/src/main_fixed.py +0 -434
- package/src/register.py +0 -131
package/src/sync.py
CHANGED
|
@@ -42,7 +42,7 @@ class ProfileSync:
|
|
|
42
42
|
with open(absolute_path, 'r', encoding='utf-8') as f:
|
|
43
43
|
return f.read()
|
|
44
44
|
except Exception as e:
|
|
45
|
-
print(f"[
|
|
45
|
+
print(f"[SoulSync] Error reading local file {file_path}: {e}")
|
|
46
46
|
return None
|
|
47
47
|
return None
|
|
48
48
|
|
|
@@ -57,7 +57,7 @@ class ProfileSync:
|
|
|
57
57
|
with open(absolute_path, 'w', encoding='utf-8') as f:
|
|
58
58
|
f.write(content)
|
|
59
59
|
except Exception as e:
|
|
60
|
-
print(f"[
|
|
60
|
+
print(f"[SoulSync] Error writing local file {file_path}: {e}")
|
|
61
61
|
|
|
62
62
|
def _create_conflict_backup(self, file_path: str, local_content: str, server_content: str):
|
|
63
63
|
"""创建冲突备份文件"""
|
|
@@ -71,64 +71,51 @@ class ProfileSync:
|
|
|
71
71
|
f.write(local_content or "(empty)")
|
|
72
72
|
f.write("\n\n========== SERVER VERSION ==========\n")
|
|
73
73
|
f.write(server_content or "(empty)")
|
|
74
|
-
print(f"[
|
|
74
|
+
print(f"[SoulSync] Conflict backup created: {conflict_path}")
|
|
75
75
|
except Exception as e:
|
|
76
|
-
print(f"[
|
|
76
|
+
print(f"[SoulSync] Error creating conflict backup: {e}")
|
|
77
77
|
|
|
78
78
|
def pull_all(self):
|
|
79
79
|
"""Pull all profiles from cloud"""
|
|
80
|
-
print("[
|
|
80
|
+
print("[SoulSync] Pulling all profiles from cloud...")
|
|
81
81
|
|
|
82
82
|
try:
|
|
83
83
|
result = self.client.get_profiles()
|
|
84
|
-
|
|
84
|
+
cloud_content = result.get('content', {})
|
|
85
|
+
cloud_version = result.get('version', 0)
|
|
85
86
|
except Exception as e:
|
|
86
|
-
print(f"[
|
|
87
|
+
print(f"[SoulSync] Error fetching cloud profiles: {e}")
|
|
87
88
|
return
|
|
88
89
|
|
|
89
|
-
if not
|
|
90
|
-
print("[
|
|
90
|
+
if not cloud_content:
|
|
91
|
+
print("[SoulSync] No profiles on cloud")
|
|
91
92
|
return
|
|
92
93
|
|
|
94
|
+
local_files = ['SOUL.md', 'USER.md', 'MEMORY.md']
|
|
93
95
|
pulled_count = 0
|
|
94
|
-
pushed_count = 0
|
|
95
96
|
skipped_count = 0
|
|
96
97
|
|
|
97
|
-
for
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if not file_path:
|
|
103
|
-
continue
|
|
104
|
-
|
|
105
|
-
local_content = self._read_local_file(file_path)
|
|
106
|
-
local_version = self.version_manager.get_version(file_path)
|
|
98
|
+
for file_name in local_files:
|
|
99
|
+
cloud_file_content = cloud_content.get(file_name, '')
|
|
100
|
+
local_content = self._read_local_file(file_name)
|
|
101
|
+
local_version = self.version_manager.get_version(file_name)
|
|
107
102
|
|
|
108
103
|
if cloud_version > local_version:
|
|
109
|
-
self._mark_syncing(
|
|
104
|
+
self._mark_syncing(file_name)
|
|
110
105
|
try:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
106
|
+
if cloud_file_content:
|
|
107
|
+
self._write_local_file(file_name, cloud_file_content)
|
|
108
|
+
self.version_manager.set_version(file_name, cloud_version)
|
|
109
|
+
pulled_count += 1
|
|
110
|
+
print(f"[SoulSync] Pulled: {file_name} (v{cloud_version})")
|
|
115
111
|
finally:
|
|
116
|
-
self._unmark_syncing(
|
|
117
|
-
elif local_version > cloud_version and local_content is not None:
|
|
118
|
-
try:
|
|
119
|
-
result = self.client.upload_profile(file_path, local_content, local_version)
|
|
120
|
-
self.version_manager.set_version(file_path, result.get('version', local_version))
|
|
121
|
-
pushed_count += 1
|
|
122
|
-
print(f"[Sync] Pushed: {file_path} (v{local_version})")
|
|
123
|
-
except ConflictError as e:
|
|
124
|
-
self._handle_conflict(file_path, local_content, e.server_content, e.server_version)
|
|
125
|
-
pushed_count += 1
|
|
126
|
-
except Exception as e:
|
|
127
|
-
print(f"[Sync] Error pushing {file_path}: {e}")
|
|
112
|
+
self._unmark_syncing(file_name)
|
|
128
113
|
else:
|
|
129
114
|
skipped_count += 1
|
|
130
115
|
|
|
131
|
-
|
|
116
|
+
self.version_manager.set_version('__profiles__', cloud_version)
|
|
117
|
+
|
|
118
|
+
print(f"[SoulSync] Sync complete: {pulled_count} pulled, {skipped_count} skipped")
|
|
132
119
|
|
|
133
120
|
def push_file(self, file_path: str):
|
|
134
121
|
"""Push a file to cloud"""
|
|
@@ -140,64 +127,84 @@ class ProfileSync:
|
|
|
140
127
|
try:
|
|
141
128
|
local_content = self._read_local_file(file_path)
|
|
142
129
|
if local_content is None:
|
|
143
|
-
print(f"[
|
|
130
|
+
print(f"[SoulSync] File not found locally: {file_path}")
|
|
144
131
|
return
|
|
145
132
|
|
|
146
133
|
local_version = self.version_manager.get_version(file_path)
|
|
147
134
|
|
|
135
|
+
profiles_version = self.version_manager.get_version('__profiles__')
|
|
136
|
+
|
|
137
|
+
current_profiles = {}
|
|
148
138
|
try:
|
|
149
|
-
result = self.client.
|
|
150
|
-
|
|
139
|
+
result = self.client.get_profiles()
|
|
140
|
+
current_profiles = result.get('content', {})
|
|
141
|
+
except Exception:
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
current_profiles[file_path] = local_content
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
result = self.client.upload_profiles(current_profiles, profiles_version)
|
|
148
|
+
new_version = result.get('version', profiles_version + 1)
|
|
149
|
+
self.version_manager.set_version('__profiles__', new_version)
|
|
151
150
|
self.version_manager.set_version(file_path, new_version)
|
|
152
|
-
print(f"[
|
|
151
|
+
print(f"[SoulSync] Pushed: {file_path} (v{new_version})")
|
|
153
152
|
except ConflictError as e:
|
|
154
153
|
self._handle_conflict(file_path, local_content, e.server_content, e.server_version)
|
|
155
154
|
except Exception as e:
|
|
156
|
-
print(f"[
|
|
155
|
+
print(f"[SoulSync] Error pushing {file_path}: {e}")
|
|
157
156
|
finally:
|
|
158
157
|
self._unmark_syncing(file_path)
|
|
159
158
|
|
|
160
159
|
def on_remote_change(self, file_path: str, version: int):
|
|
161
160
|
"""Handle remote file change"""
|
|
162
|
-
local_version = self.version_manager.get_version(
|
|
161
|
+
local_version = self.version_manager.get_version('__profiles__')
|
|
163
162
|
|
|
164
163
|
if version <= local_version:
|
|
165
|
-
print(f"[
|
|
164
|
+
print(f"[SoulSync] Remote version not newer, skipping: {file_path} (local: v{local_version}, remote: v{version})")
|
|
166
165
|
return
|
|
167
166
|
|
|
168
167
|
self._mark_syncing(file_path)
|
|
169
168
|
|
|
170
169
|
try:
|
|
171
|
-
result = self.client.get_profiles(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
170
|
+
result = self.client.get_profiles()
|
|
171
|
+
cloud_content = result.get('content', {})
|
|
172
|
+
cloud_version = result.get('version', 0)
|
|
173
|
+
|
|
174
|
+
if file_path not in cloud_content:
|
|
175
|
+
print(f"[SoulSync] File not found on cloud: {file_path}")
|
|
175
176
|
return
|
|
176
177
|
|
|
177
|
-
|
|
178
|
-
cloud_content = cloud_file.get('content', '')
|
|
178
|
+
cloud_file_content = cloud_content.get(file_path, '')
|
|
179
179
|
|
|
180
180
|
local_content = self._read_local_file(file_path)
|
|
181
181
|
|
|
182
|
-
if local_content is not None and local_content !=
|
|
183
|
-
self._create_conflict_backup(file_path, local_content,
|
|
182
|
+
if local_content is not None and local_content != cloud_file_content:
|
|
183
|
+
self._create_conflict_backup(file_path, local_content, cloud_file_content)
|
|
184
184
|
|
|
185
|
-
self._write_local_file(file_path,
|
|
185
|
+
self._write_local_file(file_path, cloud_file_content)
|
|
186
|
+
self.version_manager.set_version('__profiles__', cloud_version)
|
|
186
187
|
self.version_manager.set_version(file_path, version)
|
|
187
|
-
print(f"[
|
|
188
|
+
print(f"[SoulSync] Pulled remote change: {file_path} (v{version})")
|
|
188
189
|
except Exception as e:
|
|
189
|
-
print(f"[
|
|
190
|
+
print(f"[SoulSync] Error handling remote change for {file_path}: {e}")
|
|
190
191
|
finally:
|
|
191
192
|
self._unmark_syncing(file_path)
|
|
192
193
|
|
|
193
194
|
def _handle_conflict(self, file_path: str, local_content: str, server_content: str, server_version: int):
|
|
194
195
|
"""Handle conflict when pushing file"""
|
|
195
|
-
print(f"[
|
|
196
|
+
print(f"[SoulSync] CONFLICT detected for {file_path}")
|
|
196
197
|
|
|
197
198
|
self._create_conflict_backup(file_path, local_content, server_content)
|
|
198
199
|
|
|
199
|
-
|
|
200
|
+
if isinstance(server_content, dict):
|
|
201
|
+
server_file_content = server_content.get(file_path, '')
|
|
202
|
+
else:
|
|
203
|
+
server_file_content = server_content
|
|
204
|
+
|
|
205
|
+
self._write_local_file(file_path, server_file_content)
|
|
206
|
+
self.version_manager.set_version('__profiles__', server_version)
|
|
200
207
|
self.version_manager.set_version(file_path, server_version)
|
|
201
208
|
|
|
202
|
-
print(f"[
|
|
203
|
-
print(f"[
|
|
209
|
+
print(f"[SoulSync] Conflict resolved: server version used for {file_path}")
|
|
210
|
+
print(f"[SoulSync] Please manually merge the conflict backup file")
|
package/test_ssl_fix.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
测试 SSL 修复是否有效
|
|
4
|
+
"""
|
|
5
|
+
import ssl
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
print(f"Python 版本: {sys.version}")
|
|
9
|
+
print(f"SSL 版本: {ssl.OPENSSL_VERSION}")
|
|
10
|
+
print()
|
|
11
|
+
|
|
12
|
+
# 测试 1: 使用 ssl.create_default_context()
|
|
13
|
+
print("测试 1: 使用 ssl.create_default_context()")
|
|
14
|
+
try:
|
|
15
|
+
import urllib.request
|
|
16
|
+
ctx = ssl.create_default_context()
|
|
17
|
+
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
|
|
18
|
+
req = urllib.request.Request('https://soulsync.work/api/health')
|
|
19
|
+
response = urllib.request.urlopen(req, context=ctx)
|
|
20
|
+
print(f"✅ 成功: {response.read().decode()}")
|
|
21
|
+
except Exception as e:
|
|
22
|
+
print(f"❌ 失败: {e}")
|
|
23
|
+
|
|
24
|
+
print()
|
|
25
|
+
|
|
26
|
+
# 测试 2: 使用 requests + TLSAdapter
|
|
27
|
+
print("测试 2: 使用 requests + 自定义 TLSAdapter")
|
|
28
|
+
try:
|
|
29
|
+
import requests
|
|
30
|
+
from requests.adapters import HTTPAdapter
|
|
31
|
+
|
|
32
|
+
class TLSAdapter(HTTPAdapter):
|
|
33
|
+
def init_poolmanager(self, *args, **kwargs):
|
|
34
|
+
ctx = ssl.create_default_context()
|
|
35
|
+
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
|
|
36
|
+
kwargs['ssl_context'] = ctx
|
|
37
|
+
return super().init_poolmanager(*args, **kwargs)
|
|
38
|
+
|
|
39
|
+
session = requests.Session()
|
|
40
|
+
session.mount('https://', TLSAdapter())
|
|
41
|
+
response = session.get('https://soulsync.work/api/health')
|
|
42
|
+
print(f"✅ 成功: {response.text}")
|
|
43
|
+
except Exception as e:
|
|
44
|
+
print(f"❌ 失败: {e}")
|
|
45
|
+
|
|
46
|
+
print()
|
|
47
|
+
|
|
48
|
+
# 测试 3: 直接 requests(无适配器)
|
|
49
|
+
print("测试 3: 直接 requests(无适配器)")
|
|
50
|
+
try:
|
|
51
|
+
import requests
|
|
52
|
+
response = requests.get('https://soulsync.work/api/health')
|
|
53
|
+
print(f"✅ 成功: {response.text}")
|
|
54
|
+
except Exception as e:
|
|
55
|
+
print(f"❌ 失败: {e}")
|
|
56
|
+
|
|
57
|
+
print()
|
|
58
|
+
print("测试完成")
|
package/src/interactive_auth.py
DELETED
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
SoulSync 交互式认证模块
|
|
4
|
-
处理用户注册、登录、邮箱验证
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import os
|
|
8
|
-
import sys
|
|
9
|
-
import json
|
|
10
|
-
import re
|
|
11
|
-
import getpass
|
|
12
|
-
|
|
13
|
-
# 获取插件目录
|
|
14
|
-
PLUGIN_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
15
|
-
CONFIG_PATH = os.path.join(PLUGIN_DIR, 'config.json')
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def get_input(prompt):
|
|
19
|
-
"""获取用户输入"""
|
|
20
|
-
try:
|
|
21
|
-
return input(prompt)
|
|
22
|
-
except KeyboardInterrupt:
|
|
23
|
-
print("\n\nOperation cancelled / 操作已取消")
|
|
24
|
-
sys.exit(0)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def get_password(prompt):
|
|
28
|
-
"""获取密码(隐藏输入)"""
|
|
29
|
-
try:
|
|
30
|
-
return getpass.getpass(prompt)
|
|
31
|
-
except KeyboardInterrupt:
|
|
32
|
-
print("\n\nOperation cancelled / 操作已取消")
|
|
33
|
-
sys.exit(0)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def is_valid_email(email):
|
|
37
|
-
"""验证邮箱格式"""
|
|
38
|
-
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
|
39
|
-
return re.match(pattern, email) is not None
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def load_config():
|
|
43
|
-
"""加载配置文件"""
|
|
44
|
-
if os.path.exists(CONFIG_PATH):
|
|
45
|
-
try:
|
|
46
|
-
with open(CONFIG_PATH, 'r', encoding='utf-8') as f:
|
|
47
|
-
return json.load(f)
|
|
48
|
-
except:
|
|
49
|
-
return {}
|
|
50
|
-
return {}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def save_config(config):
|
|
54
|
-
"""保存配置文件"""
|
|
55
|
-
try:
|
|
56
|
-
with open(CONFIG_PATH, 'w', encoding='utf-8') as f:
|
|
57
|
-
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
58
|
-
return True
|
|
59
|
-
except Exception as e:
|
|
60
|
-
print(f"Failed to save config: {e} / 保存配置失败: {e}")
|
|
61
|
-
return False
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def check_existing_config():
|
|
65
|
-
"""检查是否已有配置"""
|
|
66
|
-
config = load_config()
|
|
67
|
-
email = config.get('email', '').strip()
|
|
68
|
-
password = config.get('password', '').strip()
|
|
69
|
-
|
|
70
|
-
if email and password:
|
|
71
|
-
return config
|
|
72
|
-
return None
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def interactive_setup(client):
|
|
76
|
-
"""
|
|
77
|
-
交互式设置流程
|
|
78
|
-
返回配置好的 config 字典
|
|
79
|
-
"""
|
|
80
|
-
print("\n" + "=" * 50)
|
|
81
|
-
print("Welcome to SoulSync! / 欢迎使用 SoulSync!")
|
|
82
|
-
print("=" * 50)
|
|
83
|
-
print()
|
|
84
|
-
|
|
85
|
-
# 检查现有配置
|
|
86
|
-
existing = check_existing_config()
|
|
87
|
-
if existing:
|
|
88
|
-
print(f"Detected existing account / 已检测到现有账号: {existing['email']}")
|
|
89
|
-
choice = get_input("Use existing account? (y/n) / 是否使用现有账号?: ").lower().strip()
|
|
90
|
-
if choice in ['y', 'yes', '是']:
|
|
91
|
-
return existing
|
|
92
|
-
print()
|
|
93
|
-
|
|
94
|
-
# 询问是否有账号
|
|
95
|
-
print("Please select / 请选择:")
|
|
96
|
-
print("1. Login / 登录已有账号")
|
|
97
|
-
print("2. Register / 注册新账号")
|
|
98
|
-
print()
|
|
99
|
-
|
|
100
|
-
while True:
|
|
101
|
-
choice = get_input("Enter option / 输入选项 (1/2): ").strip()
|
|
102
|
-
if choice in ['1', '2']:
|
|
103
|
-
break
|
|
104
|
-
print("Invalid option / 无效选项,请重新输入")
|
|
105
|
-
|
|
106
|
-
if choice == '1':
|
|
107
|
-
return interactive_login(client)
|
|
108
|
-
else:
|
|
109
|
-
return interactive_register(client)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def interactive_login(client):
|
|
113
|
-
"""交互式登录"""
|
|
114
|
-
print("\n--- Login / 登录 ---")
|
|
115
|
-
|
|
116
|
-
# 输入邮箱
|
|
117
|
-
while True:
|
|
118
|
-
email = get_input("Email / 邮箱: ").strip()
|
|
119
|
-
if is_valid_email(email):
|
|
120
|
-
break
|
|
121
|
-
print("Invalid email format / 邮箱格式不正确,请重新输入")
|
|
122
|
-
|
|
123
|
-
# 输入密码
|
|
124
|
-
password = get_password("Password / 密码: ")
|
|
125
|
-
|
|
126
|
-
print("\nLogging in... / 正在登录...")
|
|
127
|
-
|
|
128
|
-
try:
|
|
129
|
-
# 尝试登录
|
|
130
|
-
result = client.authenticate(email, password)
|
|
131
|
-
|
|
132
|
-
if result:
|
|
133
|
-
print("✅ Login successful! / 登录成功!")
|
|
134
|
-
|
|
135
|
-
# 保存配置
|
|
136
|
-
config = load_config()
|
|
137
|
-
config['email'] = email
|
|
138
|
-
config['password'] = password
|
|
139
|
-
save_config(config)
|
|
140
|
-
|
|
141
|
-
return config
|
|
142
|
-
else:
|
|
143
|
-
print("❌ Login failed, invalid email or password / 登录失败,邮箱或密码错误")
|
|
144
|
-
return None
|
|
145
|
-
|
|
146
|
-
except Exception as e:
|
|
147
|
-
print(f"❌ Login error: {e} / 登录出错: {e}")
|
|
148
|
-
return None
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
def interactive_register(client):
|
|
152
|
-
"""交互式注册"""
|
|
153
|
-
print("\n--- Register / 注册新账号 ---")
|
|
154
|
-
|
|
155
|
-
# 输入邮箱
|
|
156
|
-
while True:
|
|
157
|
-
email = get_input("Email / 邮箱: ").strip()
|
|
158
|
-
if not is_valid_email(email):
|
|
159
|
-
print("Invalid email format / 邮箱格式不正确,请重新输入")
|
|
160
|
-
continue
|
|
161
|
-
|
|
162
|
-
# 检查邮箱是否已注册
|
|
163
|
-
print("Checking email availability... / 检查邮箱可用性...")
|
|
164
|
-
# TODO: 调用后端 API 检查邮箱是否已存在
|
|
165
|
-
# 暂时假设可用
|
|
166
|
-
break
|
|
167
|
-
|
|
168
|
-
# 输入密码
|
|
169
|
-
while True:
|
|
170
|
-
password = get_password("Set password (min 6 characters) / 设置密码 (至少6位): ")
|
|
171
|
-
if len(password) >= 6:
|
|
172
|
-
break
|
|
173
|
-
print("Password too short, minimum 6 characters / 密码太短,请至少输入6位")
|
|
174
|
-
|
|
175
|
-
# 确认密码
|
|
176
|
-
while True:
|
|
177
|
-
password2 = get_password("Confirm password / 确认密码: ")
|
|
178
|
-
if password == password2:
|
|
179
|
-
break
|
|
180
|
-
print("Passwords do not match, please try again / 两次密码不一致,请重新输入")
|
|
181
|
-
|
|
182
|
-
# 发送验证码
|
|
183
|
-
print(f"\nSending verification code to {email} / 正在发送验证码到 {email}...")
|
|
184
|
-
try:
|
|
185
|
-
client.send_verification_code(email)
|
|
186
|
-
print("✅ Verification code sent! / 验证码已发送!")
|
|
187
|
-
except Exception as e:
|
|
188
|
-
print(f"❌ Failed to send code: {e} / 发送验证码失败: {e}")
|
|
189
|
-
return None
|
|
190
|
-
|
|
191
|
-
# 输入验证码
|
|
192
|
-
max_attempts = 3
|
|
193
|
-
for attempt in range(max_attempts):
|
|
194
|
-
code = get_input(f"Enter verification code / 请输入验证码 (remaining attempts / 剩余尝试: {max_attempts - attempt}): ").strip()
|
|
195
|
-
|
|
196
|
-
# Verify code format (6 digits)
|
|
197
|
-
if len(code) == 6 and code.isdigit():
|
|
198
|
-
# Verify with server
|
|
199
|
-
try:
|
|
200
|
-
result = client.register(email, password, code)
|
|
201
|
-
print("✅ Verification successful! / 验证成功!")
|
|
202
|
-
break
|
|
203
|
-
except Exception as e:
|
|
204
|
-
print(f"❌ Verification failed: {e} / 验证失败: {e}")
|
|
205
|
-
if attempt == max_attempts - 1:
|
|
206
|
-
print("Too many failed attempts, please re-register / 验证失败次数过多,请重新注册")
|
|
207
|
-
return None
|
|
208
|
-
else:
|
|
209
|
-
print("❌ Invalid code format, please enter 6-digit code / 验证码格式错误,请输入6位数字验证码")
|
|
210
|
-
if attempt == max_attempts - 1:
|
|
211
|
-
print("Too many failed attempts, please re-register / 验证失败次数过多,请重新注册")
|
|
212
|
-
return None
|
|
213
|
-
|
|
214
|
-
# 完成注册
|
|
215
|
-
print("\nCreating account... / 正在创建账号...")
|
|
216
|
-
try:
|
|
217
|
-
print("✅ Registration successful! / 注册成功!")
|
|
218
|
-
|
|
219
|
-
# 保存配置
|
|
220
|
-
config = load_config()
|
|
221
|
-
config['email'] = email
|
|
222
|
-
config['password'] = password
|
|
223
|
-
save_config(config)
|
|
224
|
-
|
|
225
|
-
return config
|
|
226
|
-
|
|
227
|
-
except Exception as e:
|
|
228
|
-
print(f"❌ Registration failed: {e} / 注册失败: {e}")
|
|
229
|
-
return None
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
def prompt_for_missing_config(client):
|
|
233
|
-
"""
|
|
234
|
-
当配置缺失时,提示用户输入
|
|
235
|
-
用于插件启动时自动检测
|
|
236
|
-
"""
|
|
237
|
-
config = load_config()
|
|
238
|
-
|
|
239
|
-
email = config.get('email', '').strip()
|
|
240
|
-
password = config.get('password', '').strip()
|
|
241
|
-
cloud_url = config.get('cloud_url', '').strip()
|
|
242
|
-
|
|
243
|
-
# 检查是否需要交互式配置
|
|
244
|
-
need_setup = not email or not password or not cloud_url
|
|
245
|
-
|
|
246
|
-
if need_setup:
|
|
247
|
-
print("\nFirst time using SoulSync, configuration required... / 首次使用 SoulSync,需要进行配置...")
|
|
248
|
-
|
|
249
|
-
# 如果没有 cloud_url,设置默认值
|
|
250
|
-
if not cloud_url:
|
|
251
|
-
config['cloud_url'] = 'https://soulsync.work'
|
|
252
|
-
print(f"Using default server / 使用默认服务器: {config['cloud_url']}")
|
|
253
|
-
|
|
254
|
-
# 交互式登录/注册
|
|
255
|
-
result = interactive_setup(client)
|
|
256
|
-
|
|
257
|
-
if result:
|
|
258
|
-
return result
|
|
259
|
-
else:
|
|
260
|
-
print("\n❌ Configuration failed, plugin cannot start / 配置失败,插件无法启动")
|
|
261
|
-
return None
|
|
262
|
-
|
|
263
|
-
return config
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if __name__ == '__main__':
|
|
267
|
-
# 测试代码
|
|
268
|
-
print("Interactive Auth Module Test / 交互式认证模块测试")
|
|
269
|
-
print("=" * 50)
|
|
270
|
-
|
|
271
|
-
# 模拟客户端
|
|
272
|
-
class MockClient:
|
|
273
|
-
def authenticate(self, email, password):
|
|
274
|
-
print(f"Simulating login / 模拟登录: {email}")
|
|
275
|
-
return True
|
|
276
|
-
|
|
277
|
-
client = MockClient()
|
|
278
|
-
result = interactive_setup(client)
|
|
279
|
-
|
|
280
|
-
if result:
|
|
281
|
-
print(f"\nConfiguration complete / 配置完成: {result}")
|
|
282
|
-
else:
|
|
283
|
-
print("\nConfiguration failed / 配置失败")
|