1
1
import os
2
2
import re
3
+ from openai import OpenAI
4
+
5
+ class Skill :
6
+ name : str
7
+ contents = []
8
+ translations = []
9
+ def __init__ (self , name , contents , trans ):
10
+ self .name = name
11
+ self .contents = contents
12
+ self .translations = trans
13
+
14
+ class GPT :
15
+ basic_prompt : str # 用来指示技能基本架构如何进行改变的prompt
16
+ misc_prompt : str
17
+ askto_prompt : str # 用来杀askFor系列的prompt
18
+ model = "qwen2.5:32b_72Kctx" # 用ollama创建的context window增大版的model
19
+
20
+ def __init__ (self ):
21
+ dir_path = os .path .dirname (os .path .abspath (__file__ ))
22
+ base_path = os .path .join (dir_path , "prompts" )
23
+ with open (os .path .join (base_path , "01-basic.md" )) as f :
24
+ self .basic_prompt = f .read ()
25
+ with open (os .path .join (base_path , "02-misc.md" )) as f :
26
+ self .misc_prompt = f .read ()
27
+ with open (os .path .join (base_path , "03-askto.md" )) as f :
28
+ self .askto_prompt = f .read ()
29
+
30
+ # 根据自己需求修改key
31
+ # 不过如果用的是公开厂商的API key,那么绝对不要公布出来!
32
+ # 这里是我本地部署的ai所以我就对key随便处理了
33
+ api_end_point = "http://localhost:8080/api"
34
+ api_key = "sk-cb7ee040c840447ca35cc6e63c25bebf"
35
+ self .client = OpenAI (api_key = api_key , base_url = api_end_point )
36
+
37
+ def chat (self , sys_prompt , skill ) -> str :
38
+ return self .client .chat .completions .create (
39
+ model = self .model ,
40
+ messages = [
41
+ {"role" : "system" , "content" : sys_prompt },
42
+ {"role" : "user" , "content" : skill },
43
+ ],
44
+ stream = False
45
+ ).choices [0 ].message .content
3
46
4
47
# 先就解决一下提取技能和提取武将的问题
5
48
class FkPackage :
6
- skills = {}
49
+ skills : dict [ str , Skill ] = {}
7
50
generals = []
8
51
translations = {}
9
52
10
53
def __init__ (self , filepath : str ):
54
+ self .gpt = GPT ()
11
55
self .filepath = filepath
12
56
with open (filepath , 'r' , encoding = 'utf-8' ) as f :
13
57
self .file_content = f .read ()
14
58
self ._extract_translations ()
15
59
self ._extract_skills ()
16
- self ._extract_generals ()
60
+
61
+ '''
62
+ 直接检索文件中的Fk:loadTranslationTable { ... }
63
+ 花括号中的内容必定是形如 ["xxx"] = "xxx2", 之类的表形式
64
+ 并且必定不产生嵌套 只有string-string的键值对
65
+ 可能利用了lua的字符串拼接符号..进行多行字符串,亦可能使用单引号
66
+ 将提取到的翻译表内容翻译成python字典语法,然后直接读取该字典
67
+ '''
68
+ def _extract_translations (self ):
69
+ self .translations = {}
70
+ # 查找Fk:loadTranslationTable的位置
71
+ start_pattern = re .compile (r'Fk:loadTranslationTable\s*{' , re .DOTALL )
72
+ for match in start_pattern .finditer (self .file_content ):
73
+ start_pos = match .end ()
74
+
75
+ # 提取直到匹配的闭合花括号的内容
76
+ brace_count = 1
77
+ current_pos = start_pos
78
+ content = []
79
+ while current_pos < len (self .file_content ) and brace_count > 0 :
80
+ char = self .file_content [current_pos ]
81
+ if char == '{' :
82
+ brace_count += 1
83
+ elif char == '}' :
84
+ brace_count -= 1
85
+ if brace_count > 0 :
86
+ content .append (char )
87
+ current_pos += 1
88
+ table_content = '' .join (content )
89
+
90
+ # 去除注释
91
+ cleaned_content = re .sub (r'--.*' , '' , table_content , flags = re .MULTILINE )
92
+
93
+ # 分割键值对
94
+ entries = re .split (r',\s*' , cleaned_content )
95
+
96
+ for entry in entries :
97
+ entry = entry .strip ()
98
+ if not entry :
99
+ continue
100
+
101
+ # 解析键
102
+ key_match = re .match (r'\s*\[\s*(["\'])(.*?)\1\s*\]\s*=\s*(.*)' , entry , re .DOTALL )
103
+ if not key_match :
104
+ continue
105
+ quote_type , key_content , value_expr = key_match .groups ()
106
+
107
+ # 处理键的转义
108
+ key = self ._unescape_lua_string (key_content )
109
+
110
+ # 解析值中的字符串
111
+ value = self ._parse_lua_value (value_expr )
112
+
113
+ if key and value is not None :
114
+ self .translations [key ] = value
115
+
116
+ def _unescape_lua_string (self , s ):
117
+ # 处理Lua字符串中的转义字符
118
+ replacements = {
119
+ '\\ \\ ' : '\\ ' ,
120
+ '\\ "' : '"' ,
121
+ "\\ '" : "'" ,
122
+ '\\ n' : '\n ' ,
123
+ '\\ t' : '\t ' ,
124
+ '\\ r' : '\r ' ,
125
+ '\\ b' : '\b ' ,
126
+ '\\ f' : '\f ' ,
127
+ '\\ a' : '\a ' ,
128
+ '\\ v' : '\v '
129
+ }
130
+ for escaped , unescaped in replacements .items ():
131
+ s = s .replace (escaped , unescaped )
132
+ return s
133
+
134
+ def _parse_lua_value (self , expr ):
135
+ # 解析Lua字符串拼接表达式
136
+ str_pattern = re .compile (r'''["']((?:[^\\"']|\\.)*)["']''' , re .DOTALL )
137
+ parts = []
138
+ for match in str_pattern .finditer (expr ):
139
+ str_content = match .group (1 )
140
+ parts .append (self ._unescape_lua_string (str_content ))
141
+ return '' .join (parts ) if parts else None
17
142
18
143
'''
19
144
把东西作为字典放入自己的skills里面
@@ -34,14 +159,6 @@ def __init__(self, filepath: str):
34
159
35
160
最后加入数组
36
161
'''
37
- def _extract_translations (self ):
38
- # 提取所有翻译项
39
- trans_pattern = re .compile (r'\[["\'](.*?)["\']\]\s*=\s*["\'](.*?)["\']' , re .DOTALL )
40
- self .translations = {}
41
- for match in trans_pattern .finditer (self .file_content ):
42
- key , value = match .groups ()
43
- self .translations [key ] = value
44
-
45
162
def _extract_skills (self ):
46
163
self .skills = {}
47
164
skill_pattern = re .compile (r'local\s+(\w+)\s*=\s*fk\.Create\w+Skill\s*\{' , re .IGNORECASE )
@@ -58,10 +175,8 @@ def _extract_skills(self):
58
175
level -= 1
59
176
end_pos += 1
60
177
content = self .file_content [start_pos :end_pos ] # 提取完整技能内容
61
- self .skills [skill_name ] = ({
62
- 'content' : content ,
63
- 'translations' : [],
64
- })
178
+ skill_str_name = re .compile (r'name = ["\'](.*?)["\'],' ).findall (content )[0 ]
179
+ self .skills [skill_name ] = Skill (skill_str_name , [content ], [])
65
180
66
181
related_pattern = re .compile (r'(\w+):addRelatedSkill\(\s*(\w+)\s*\)' )
67
182
for match in related_pattern .finditer (self .file_content ):
@@ -71,67 +186,29 @@ def _extract_skills(self):
71
186
continue
72
187
main_skill_obj = self .skills [main_skill ]
73
188
sub_skill_obj = self .skills [sub_skill ]
74
- main_skill_obj ['content' ] += "\n \n "
75
- main_skill_obj ['content' ] += sub_skill_obj ['content' ]
76
- main_skill_obj ['content' ] += "\n "
77
- main_skill_obj ['content' ] += match .group ()
189
+ main_skill_obj .contents .append (sub_skill_obj .contents [0 ])
78
190
del self .skills [sub_skill ]
79
191
80
192
for k in self .skills :
81
193
sk = self .skills [k ]
82
- content = sk ['content' ]
83
- strings = re .findall (r'["\'](.*?)["\']' , content , re .DOTALL )
194
+ content = '\n ' .join (sk .contents )
195
+ strings : list [str ] = re .findall (r'["\'](.*?)["\']' , content , re .DOTALL )
196
+ for i in range (0 , len (strings )):
197
+ if strings [i ].endswith (':' ):
198
+ strings [i ] = strings [i ].rstrip (':' )
199
+ strings .append (sk .name )
200
+ strings .append (":" + sk .name )
201
+ strings .append ("$" + sk .name )
202
+ for i in range (1 , 9 ):
203
+ strings .append ("$" + sk .name + str (i ))
84
204
# 收集翻译并去重
85
205
translations = []
86
206
seen = set ()
87
- for s in set ( strings ) :
207
+ for s in strings :
88
208
if s in self .translations and s not in seen :
89
209
seen .add (s )
90
210
translations .append ({'key' : s , 'value' : self .translations [s ]})
91
- sk ['translations' ] = translations
92
-
93
- def _extract_generals (self ):
94
- self .generals = []
95
- general_pattern = re .compile (r'local\s+(\w+)\s*=\s*General(?::new)?\s*\(' , re .IGNORECASE )
96
- for match in general_pattern .finditer (self .file_content ):
97
- general_name = match .group (1 )
98
- # 定位参数括号位置
99
- general_match = match .group (0 )
100
- paren_pos = general_match .rfind ('(' )
101
- start_paren_pos = match .start () + paren_pos
102
- level = 1
103
- end_paren_pos = start_paren_pos + 1
104
- while end_paren_pos < len (self .file_content ) and level > 0 :
105
- char = self .file_content [end_paren_pos ]
106
- if char == '(' :
107
- level += 1
108
- elif char == ')' :
109
- level -= 1
110
- end_paren_pos += 1
111
- creation_content = self .file_content [match .start ():end_paren_pos ]
112
- # 收集关联调用
113
- related_lines = []
114
- line_pattern = re .compile (r'^\s*{}\b\s*[:.]' .format (re .escape (general_name )), re .MULTILINE )
115
- for line_match in line_pattern .finditer (self .file_content , end_paren_pos ):
116
- line_start = line_match .start ()
117
- line_end = self .file_content .find ('\n ' , line_start )
118
- line = self .file_content [line_start :line_end if line_end != - 1 else None ].strip ()
119
- related_lines .append (line )
120
- # 合并内容
121
- content = '\n ' .join ([creation_content ] + related_lines )
122
- # 处理翻译
123
- strings = re .findall (r'["\'](.*?)["\']' , content , re .DOTALL )
124
- translations = []
125
- seen = set ()
126
- for s in set (strings ):
127
- if s in self .translations and s not in seen :
128
- seen .add (s )
129
- translations .append ({'key' : s , 'value' : self .translations [s ]})
130
- self .generals .append ({
131
- 'name' : general_name ,
132
- 'content' : content ,
133
- 'translations' : translations
134
- })
211
+ sk .translations = translations
135
212
136
213
def mkSkillDir (self ):
137
214
"""创建技能文件目录结构并写入技能内容"""
@@ -143,15 +220,41 @@ def mkSkillDir(self):
143
220
target_dir = os .path .join (dir_path , "pkg" , base_name , "skills" )
144
221
os .makedirs (target_dir , exist_ok = True )
145
222
223
+ i = 0
224
+ n = len (self .skills .keys ())
225
+
146
226
# 写入技能文件
147
227
for skill_name in self .skills :
228
+ i += 1
229
+ print ("[*] 正在处理第 %d/%d 个技能:%s" % (i , n , skill_name ))
148
230
skill = self .skills [skill_name ]
149
231
# 清理非法文件名字符
150
232
safe_name = re .sub (r'[\\/*?:"<>|]' , '_' , skill_name )
151
233
file_path = os .path .join (target_dir , f"{ safe_name } .lua" )
152
234
153
235
try :
154
236
with open (file_path , 'w' , encoding = 'utf-8' ) as f :
155
- f .write (skill ['content' ])
237
+ f .write (self ._refactor_skill (skill ))
238
+
156
239
except Exception as e :
157
240
print (f"写入技能文件失败: { file_path } | 错误: { str (e )} " )
241
+
242
+ def _refactor_skill (self , skillObj : Skill ) -> str :
243
+ content = '\n \n ' .join (skillObj .contents )
244
+ content += "Fk:loadTranslationTable{\n "
245
+ for kv in skillObj .translations :
246
+ content += " [%s] = %s,\n " % (repr (kv ['key' ]), repr (kv ['value' ]))
247
+ content += "}\n "
248
+ gpt = self .gpt
249
+ n = 2
250
+
251
+ print (" [1/%d] 将技能主体结构调整为新版" % n )
252
+ content = gpt .chat (gpt .basic_prompt , content )
253
+ print (" [2/%d] 调整self.name,cost_data等" % n )
254
+ content = gpt .chat (gpt .misc_prompt , content )
255
+ print (" [3/%d] 重构askFor系列函数" % n )
256
+ content = gpt .chat (gpt .askto_prompt , content )
257
+
258
+ content = content .replace (' ' , ' ' )
259
+
260
+ return content
0 commit comments