pymud 0.21.0a4__py3-none-any.whl → 0.21.0a5__py3-none-any.whl
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.
- pymud/__init__.py +5 -5
- pymud/decorators.py +234 -0
- pymud/extras.py +1 -1
- pymud/lang/i18n_chs.py +6 -2
- pymud/lang/i18n_eng.py +13 -6
- pymud/main.py +17 -4
- pymud/modules.py +33 -180
- pymud/objects.py +77 -70
- pymud/protocol.py +7 -7
- pymud/pymud.py +3 -3
- pymud/session.py +182 -154
- pymud/settings.py +2 -2
- {pymud-0.21.0a4.dist-info → pymud-0.21.0a5.dist-info}/METADATA +33 -4
- pymud-0.21.0a5.dist-info/RECORD +23 -0
- pymud-0.21.0a4.dist-info/RECORD +0 -22
- {pymud-0.21.0a4.dist-info → pymud-0.21.0a5.dist-info}/WHEEL +0 -0
- {pymud-0.21.0a4.dist-info → pymud-0.21.0a5.dist-info}/entry_points.txt +0 -0
- {pymud-0.21.0a4.dist-info → pymud-0.21.0a5.dist-info}/licenses/LICENSE.txt +0 -0
- {pymud-0.21.0a4.dist-info → pymud-0.21.0a5.dist-info}/top_level.txt +0 -0
pymud/session.py
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
-
import asyncio, logging, re, math, os, pickle, datetime, sysconfig, time, dataclasses
|
1
|
+
import asyncio, logging, re, math, os, pickle, datetime, sysconfig, time, dataclasses
|
2
|
+
from pathlib import Path
|
2
3
|
from collections.abc import Iterable
|
3
4
|
from collections import OrderedDict
|
4
|
-
import logging
|
5
5
|
from prompt_toolkit.utils import get_cwidth
|
6
6
|
from wcwidth import wcswidth, wcwidth
|
7
|
-
from typing import Union, Optional, Any, List, Tuple, Dict
|
7
|
+
from typing import Union, Optional, Any, List, Tuple, Dict, Type
|
8
8
|
from .logger import Logger
|
9
9
|
from .extras import SessionBuffer, DotDict
|
10
10
|
from .protocol import MudClientProtocol
|
11
|
-
from .modules import ModuleInfo
|
11
|
+
from .modules import ModuleInfo, Plugin
|
12
12
|
from .objects import BaseObject, Trigger, Alias, Command, Timer, SimpleAlias, SimpleTrigger, SimpleTimer, GMCPTrigger, CodeBlock, CodeLine
|
13
13
|
from .settings import Settings
|
14
|
-
|
14
|
+
from .decorators import exception, async_exception
|
15
15
|
|
16
16
|
class Session:
|
17
17
|
"""
|
@@ -102,6 +102,7 @@ class Session:
|
|
102
102
|
"t+" : "ignore",
|
103
103
|
"t-" : "ignore",
|
104
104
|
"show": "test",
|
105
|
+
"echo": "test",
|
105
106
|
}
|
106
107
|
|
107
108
|
def __init__(self, app, name, host, port, encoding = None, after_connect = None, loop = None, **kwargs):
|
@@ -163,15 +164,28 @@ class Session:
|
|
163
164
|
|
164
165
|
# 将变量加载和脚本加载调整到会话创建时刻
|
165
166
|
if Settings.client["var_autoload"]:
|
166
|
-
|
167
|
+
muddir = Path.cwd().joinpath('save')
|
168
|
+
if not muddir.exists() or not muddir.is_dir():
|
169
|
+
muddir.mkdir()
|
170
|
+
|
171
|
+
# 处理老版本当前目录的.mud文件,移动到save目录下
|
172
|
+
file = f"{self.name}.mud"
|
173
|
+
new_loc_file = muddir.joinpath(file)
|
174
|
+
if not os.path.exists(new_loc_file):
|
167
175
|
if os.path.exists(file):
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
176
|
+
os.rename(file, new_loc_file)
|
177
|
+
else:
|
178
|
+
if os.path.exists(file):
|
179
|
+
os.remove(file)
|
180
|
+
|
181
|
+
if os.path.exists(new_loc_file):
|
182
|
+
with open(new_loc_file, "rb") as fp:
|
183
|
+
try:
|
184
|
+
vars = pickle.load(fp)
|
185
|
+
self._variables.update(vars)
|
186
|
+
self.info(Settings.gettext("msg_var_autoload_success", file))
|
187
|
+
except Exception as e:
|
188
|
+
self.warning(Settings.gettext("msg_var_autoload_fail", file, e))
|
175
189
|
|
176
190
|
|
177
191
|
if self._auto_script:
|
@@ -209,7 +223,7 @@ class Session:
|
|
209
223
|
async def connect(self):
|
210
224
|
"创建到远程服务器的连接,异步非阻塞方式。"
|
211
225
|
def _protocol_factory():
|
212
|
-
return MudClientProtocol(self, onDisconnected = self.onDisconnected)
|
226
|
+
return MudClientProtocol(self, onDisconnected = self.onDisconnected, encoding = self.encoding, encoding_errors = Settings.server["encoding_errors"])
|
213
227
|
|
214
228
|
try:
|
215
229
|
#self.loop = asyncio.get_running_loop()
|
@@ -997,7 +1011,8 @@ class Session:
|
|
997
1011
|
|
998
1012
|
:param cmdtext: 纯文本命令
|
999
1013
|
"""
|
1000
|
-
|
1014
|
+
keepEval = True
|
1015
|
+
notHandle = True
|
1001
1016
|
|
1002
1017
|
# fix bugs, commands filter for enabled and sorted for priority
|
1003
1018
|
avai_cmds = [cmd for cmd in self._commands.values() if isinstance(cmd, Command) and cmd.enabled]
|
@@ -1006,15 +1021,16 @@ class Session:
|
|
1006
1021
|
for command in self._commands.values():
|
1007
1022
|
state = command.match(cmdtext)
|
1008
1023
|
if state.result == Command.SUCCESS:
|
1024
|
+
notHandle = False
|
1009
1025
|
# 命令的任务名称采用命令id,以便于后续查错
|
1010
1026
|
self.create_task(command.execute(cmdtext), name = "task-{0}".format(command.id))
|
1011
|
-
isNotCmd = False
|
1012
|
-
break
|
1013
1027
|
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1028
|
+
if not command.keepEval:
|
1029
|
+
keepEval = False
|
1030
|
+
break
|
1017
1031
|
|
1032
|
+
# 若持续匹配,再判断是否是别名
|
1033
|
+
if keepEval:
|
1018
1034
|
# fix bugs, aliases filter for enabled and sorted for priority, and add oneShot, keepEval judge
|
1019
1035
|
avai_alis = [ali for ali in self._aliases.values() if isinstance(ali, Alias) and ali.enabled]
|
1020
1036
|
avai_alis.sort(key = lambda ali: ali.priority)
|
@@ -1022,16 +1038,16 @@ class Session:
|
|
1022
1038
|
for alias in avai_alis:
|
1023
1039
|
state = alias.match(cmdtext)
|
1024
1040
|
if state.result == Alias.SUCCESS:
|
1025
|
-
|
1041
|
+
notHandle = False
|
1026
1042
|
if alias.oneShot:
|
1027
1043
|
self.delAlias(alias.id)
|
1028
1044
|
|
1029
1045
|
if not alias.keepEval:
|
1030
1046
|
break
|
1031
1047
|
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1048
|
+
# 都前面都未被处理,则直接发送
|
1049
|
+
if notHandle:
|
1050
|
+
self.writeline(cmdtext)
|
1035
1051
|
|
1036
1052
|
async def exec_text_async(self, cmdtext: str):
|
1037
1053
|
"""
|
@@ -1040,7 +1056,8 @@ class Session:
|
|
1040
1056
|
异步调用时,该函数要等待对应的代码执行完毕后才会返回。可以用于确保命令执行完毕。
|
1041
1057
|
"""
|
1042
1058
|
result = None
|
1043
|
-
|
1059
|
+
keepEval = True
|
1060
|
+
notHandle = True
|
1044
1061
|
|
1045
1062
|
# fix bugs, commands filter for enabled and sorted for priority
|
1046
1063
|
avai_cmds = [cmd for cmd in self._commands.values() if isinstance(cmd, Command) and cmd.enabled]
|
@@ -1051,12 +1068,13 @@ class Session:
|
|
1051
1068
|
if state.result == Command.SUCCESS:
|
1052
1069
|
# 命令的任务名称采用命令id,以便于后续查错
|
1053
1070
|
result = await self.create_task(command.execute(cmdtext), name = "task-{0}".format(command.id))
|
1054
|
-
|
1055
|
-
|
1071
|
+
notHandle = False
|
1072
|
+
if not command.keepEval:
|
1073
|
+
keepEval = False
|
1074
|
+
break
|
1056
1075
|
|
1057
1076
|
# 再判断是否是别名
|
1058
|
-
if
|
1059
|
-
notAlias = True
|
1077
|
+
if keepEval:
|
1060
1078
|
|
1061
1079
|
# fix bugs, aliases filter for enabled and sorted for priority, and add oneShot, keepEval judge
|
1062
1080
|
avai_alis = [ali for ali in self._aliases.values() if isinstance(ali, Alias) and ali.enabled]
|
@@ -1065,16 +1083,16 @@ class Session:
|
|
1065
1083
|
for alias in avai_alis:
|
1066
1084
|
state = alias.match(cmdtext)
|
1067
1085
|
if state.result == Alias.SUCCESS:
|
1068
|
-
|
1086
|
+
notHandle = False
|
1069
1087
|
if alias.oneShot:
|
1070
1088
|
self.delAlias(alias.id)
|
1071
1089
|
|
1072
1090
|
if not alias.keepEval:
|
1073
1091
|
break
|
1074
1092
|
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1093
|
+
# 若均为处理则是普通命令,直接发送
|
1094
|
+
if notHandle:
|
1095
|
+
self.writeline(cmdtext)
|
1078
1096
|
|
1079
1097
|
return result
|
1080
1098
|
|
@@ -1168,39 +1186,49 @@ class Session:
|
|
1168
1186
|
"""
|
1169
1187
|
return "{0}_{1}".format(prefix, self.getUniqueNumber())
|
1170
1188
|
|
1171
|
-
def enableGroup(self, group: str, enabled = True):
|
1189
|
+
def enableGroup(self, group: str, enabled = True, subgroup = True, types: Union[Type, Union[Tuple, List]] = (Alias, Trigger, Command, Timer, GMCPTrigger)):
|
1172
1190
|
"""
|
1173
1191
|
使能或禁用Group中所有对象, 返回组内各对象个数。
|
1174
1192
|
|
1175
1193
|
:param group: 组名,即各对象的 group 属性的值
|
1176
1194
|
:param enabled: 使能/禁用开关。为True时表示使能, False为禁用
|
1195
|
+
:param subgroup: 是否对子组同时生效,默认为True。子组是指名称在父组名之后的用.xxx命名的组。例如, 组 group1.group2 是 group1 的子组。
|
1196
|
+
:param types: 要使能的对象类型,默认为 (Alias, Trigger, Command, Timer, GMCPTrigger)。
|
1197
|
+
可以指定为单个类型,也可以指定为类型列表或元组。
|
1198
|
+
若指定为单个类型,则只使能该类型的对象。
|
1199
|
+
若指定为类型列表或元组,则使能该类型列表或元组中的所有类型的对象。
|
1177
1200
|
:return: 5个整数的列表,依次表示改组内操作的 别名,触发器,命令,定时器,GMCP 的个数
|
1178
1201
|
"""
|
1179
1202
|
counts = [0, 0, 0, 0, 0]
|
1180
|
-
|
1181
|
-
|
1182
|
-
ali.
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1203
|
+
if (Alias == types) or (isinstance(types, Union[List, Tuple]) and (Alias in types)):
|
1204
|
+
for ali in self._aliases.values():
|
1205
|
+
if isinstance(ali, Alias) and ((ali.group == group) or (subgroup and ali.group.startswith(group + "."))):
|
1206
|
+
ali.enabled = enabled
|
1207
|
+
counts[0] += 1
|
1208
|
+
|
1209
|
+
if (Trigger == types) or (isinstance(types, Union[List, Tuple]) and (Trigger in types)):
|
1210
|
+
for tri in self._triggers.values():
|
1211
|
+
if isinstance(tri, Trigger) and ((tri.group == group) or (subgroup and tri.group.startswith(group + "."))):
|
1212
|
+
tri.enabled = enabled
|
1213
|
+
counts[1] += 1
|
1214
|
+
|
1215
|
+
if (Command == types) or (isinstance(types, Union[List, Tuple]) and (Command in types)):
|
1216
|
+
for cmd in self._commands.values():
|
1217
|
+
if isinstance(cmd, Command) and ((cmd.group == group) or (subgroup and cmd.group.startswith(group + "."))):
|
1218
|
+
cmd.enabled = enabled
|
1219
|
+
counts[2] += 1
|
1220
|
+
|
1221
|
+
if (Timer == types) or (isinstance(types, Union[List, Tuple]) and (Timer in types)):
|
1222
|
+
for tmr in self._timers.values():
|
1223
|
+
if isinstance(tmr, Timer) and ((tmr.group == group) or (subgroup and tmr.group.startswith(group + "."))):
|
1224
|
+
tmr.enabled = enabled
|
1225
|
+
counts[3] += 1
|
1226
|
+
|
1227
|
+
if (GMCPTrigger == types) or (isinstance(types, Union[List, Tuple]) and (GMCPTrigger in types)):
|
1228
|
+
for gmcp in self._gmcp.values():
|
1229
|
+
if isinstance(gmcp, GMCPTrigger) and ((gmcp.group == group) or (subgroup and gmcp.group.startswith(group + "."))):
|
1230
|
+
gmcp.enabled = enabled
|
1231
|
+
counts[4] += 1
|
1204
1232
|
|
1205
1233
|
return counts
|
1206
1234
|
|
@@ -2234,6 +2262,10 @@ class Session:
|
|
2234
2262
|
obj.enabled = False
|
2235
2263
|
self.info(Settings.gettext("msg_object_disabled", obj.__repr__()))
|
2236
2264
|
elif args[1] == "del":
|
2265
|
+
if hasattr(obj, "__unload__"):
|
2266
|
+
obj.__unload__()
|
2267
|
+
if hasattr(obj, "unload"):
|
2268
|
+
obj.unload()
|
2237
2269
|
obj.enabled = False
|
2238
2270
|
objs.pop(args[0])
|
2239
2271
|
self.info(Settings.gettext("msg_object_deleted", obj.__repr__()))
|
@@ -2402,7 +2434,7 @@ class Session:
|
|
2402
2434
|
|
2403
2435
|
self._handle_objs("Trigger", self._triggers, *code.code[2:])
|
2404
2436
|
|
2405
|
-
def handle_task(self, code: CodeLine, *args, **kwargs):
|
2437
|
+
def handle_task(self, code: Optional[CodeLine] = None, *args, **kwargs):
|
2406
2438
|
'''
|
2407
2439
|
嵌入命令 #task 的执行函数,显示当前管理的所有任务清单(仅用于调试)。
|
2408
2440
|
该函数不应该在代码中直接调用。
|
@@ -2441,6 +2473,9 @@ class Session:
|
|
2441
2473
|
- ``#t+ mygroup``: 使能名称为 mygroup 的组内的所有对象,包括别名、触发器、命令、定时器、GMCPTrigger等
|
2442
2474
|
- ``#t- mygroup``: 禁用名称为 mygroup 的组内的所有对象,包括别名、触发器、命令、定时器、GMCPTrigger等
|
2443
2475
|
|
2476
|
+
注意:
|
2477
|
+
使用#t+/#t-调用时,相当于enableGroup传递的默认参数,即subgroup为True, 且types为所有类型。
|
2478
|
+
|
2444
2479
|
相关命令:
|
2445
2480
|
- #trigger
|
2446
2481
|
- #alias
|
@@ -2472,7 +2507,7 @@ class Session:
|
|
2472
2507
|
cnts = self.enableGroup(groupname, False)
|
2473
2508
|
self.info(Settings.gettext("msg_group_disabled", groupname, *cnts))
|
2474
2509
|
|
2475
|
-
def handle_repeat(self, code: CodeLine, *args, **kwargs):
|
2510
|
+
def handle_repeat(self, code: Optional[CodeLine] = None, *args, **kwargs):
|
2476
2511
|
'''
|
2477
2512
|
嵌入命令 #repeat / #rep 的执行函数,重复向session输出上一次人工输入的命令。
|
2478
2513
|
该函数不应该在代码中直接调用。
|
@@ -2676,7 +2711,13 @@ class Session:
|
|
2676
2711
|
def _load_module(self, module_name):
|
2677
2712
|
"加载指定名称模块"
|
2678
2713
|
try:
|
2679
|
-
if module_name
|
2714
|
+
if module_name in self.application.plugins.keys():
|
2715
|
+
plugin = self.application.plugins[module_name]
|
2716
|
+
if isinstance(plugin, Plugin):
|
2717
|
+
plugin.onSessionCreate(self)
|
2718
|
+
self.info(Settings.gettext("msg_plugin_loaded", module_name))
|
2719
|
+
|
2720
|
+
elif module_name not in self._modules.keys():
|
2680
2721
|
self._modules[module_name] = ModuleInfo(module_name, self)
|
2681
2722
|
|
2682
2723
|
else:
|
@@ -2714,6 +2755,12 @@ class Session:
|
|
2714
2755
|
if isinstance(mod, ModuleInfo):
|
2715
2756
|
mod.unload()
|
2716
2757
|
|
2758
|
+
elif module_name in self.application.plugins.keys():
|
2759
|
+
plugin = self.application.plugins[module_name]
|
2760
|
+
if isinstance(plugin, Plugin):
|
2761
|
+
plugin.onSessionDestroy(self)
|
2762
|
+
self.info(Settings.gettext("msg_plugin_unloaded", module_name))
|
2763
|
+
|
2717
2764
|
else:
|
2718
2765
|
self.warning(Settings.gettext("msg_module_not_loaded", module_name))
|
2719
2766
|
|
@@ -2830,7 +2877,7 @@ class Session:
|
|
2830
2877
|
|
2831
2878
|
elif mod in self.plugins.keys():
|
2832
2879
|
self.application.reload_plugin(self.plugins[mod])
|
2833
|
-
self.info(Settings.gettext("
|
2880
|
+
self.info(Settings.gettext("msg_plugins_reloaded", mod))
|
2834
2881
|
else:
|
2835
2882
|
self.warning(Settings.gettext("msg_name_not_found", mod))
|
2836
2883
|
|
@@ -2943,7 +2990,11 @@ class Session:
|
|
2943
2990
|
- #variable
|
2944
2991
|
'''
|
2945
2992
|
|
2946
|
-
|
2993
|
+
muddir = Path.cwd().joinpath('save')
|
2994
|
+
if not muddir.exists() or not muddir.is_dir():
|
2995
|
+
muddir.mkdir()
|
2996
|
+
|
2997
|
+
file = muddir.joinpath(f"{self.name}.mud")
|
2947
2998
|
|
2948
2999
|
with open(file, "wb") as fp:
|
2949
3000
|
saved = dict()
|
@@ -2969,28 +3020,32 @@ class Session:
|
|
2969
3020
|
|
2970
3021
|
self.buffer.text = ""
|
2971
3022
|
|
3023
|
+
@exception
|
2972
3024
|
def handle_test(self, code: CodeLine, *args, **kwargs):
|
2973
3025
|
'''
|
2974
|
-
嵌入命令 #test / #show 的执行函数,触发器测试命令。类似于zmud的#show命令。
|
3026
|
+
嵌入命令 #test / #show / #echo 的执行函数,触发器测试命令。类似于zmud的#show命令。
|
2975
3027
|
该函数不应该在代码中直接调用。
|
2976
3028
|
|
2977
3029
|
使用:
|
2978
|
-
- #show {some_text}:
|
3030
|
+
- #show {some_text}: 测试收到服务器{some_text}时的触发器响应情况。此时,触发器不会真的响应。
|
2979
3031
|
- #test {some_test}: 与#show 的差异是,若存在匹配的触发器,无论其是否被使能,该触发器均会实际响应。
|
3032
|
+
- #echo {some_text}: 模拟收到服务器 {some_text}的情况,触发器按正常情况响应,但不会显示测试结果。
|
2980
3033
|
|
2981
3034
|
示例:
|
2982
|
-
- ``#show 你深深吸了口气,站了起来。`` :
|
3035
|
+
- ``#show 你深深吸了口气,站了起来。`` : 模拟收到服务器“你深深吸了口气,站了起来。”时的情况进行触发测试(仅显示触发测试情况)
|
2983
3036
|
- ``#test %copy``: 复制一句话,模拟服务器再次收到复制的这句内容时的情况进行触发器测试
|
2984
|
-
- ``#test 你深深吸了口气,站了起来。`` :
|
3037
|
+
- ``#test 你深深吸了口气,站了起来。`` : 模拟收到服务器“你深深吸了口气,站了起来。”时的情况进行触发测试(会实际导致触发器动作)
|
3038
|
+
- ``#echo 你深深吸了口气,站了起来。`` : 模拟收到服务器“你深深吸了口气,站了起来。”时的情况进行触发测试(不会显示测试结果)
|
2985
3039
|
|
2986
3040
|
注意:
|
2987
3041
|
- #show命令测试触发器时,触发器不会真的响应。
|
2988
3042
|
- #test命令测试触发器时,触发器无论是否使能,均会真的响应。
|
3043
|
+
- #echo命令可以用来人工激发触发器。
|
2989
3044
|
'''
|
2990
3045
|
cmd = code.code[1].lower()
|
2991
|
-
docallback =
|
2992
|
-
if cmd == "
|
2993
|
-
docallback =
|
3046
|
+
docallback = True
|
3047
|
+
if cmd == "show":
|
3048
|
+
docallback = False
|
2994
3049
|
|
2995
3050
|
new_cmd_text, new_code = code.expand(self, *args, **kwargs)
|
2996
3051
|
line = new_cmd_text[6:] # 取出#test 之后的所有内容
|
@@ -3016,6 +3071,10 @@ class Session:
|
|
3016
3071
|
tris_disabled.sort(key = lambda tri: tri.priority)
|
3017
3072
|
|
3018
3073
|
for raw_line in lines:
|
3074
|
+
# echo 模式下,直接将原始数据输出到窗口,并进行触发测试
|
3075
|
+
if cmd == "echo":
|
3076
|
+
self.writetobuffer(raw_line, True)
|
3077
|
+
|
3019
3078
|
tri_line = self.getPlainText(raw_line)
|
3020
3079
|
|
3021
3080
|
block = False
|
@@ -3025,7 +3084,7 @@ class Session:
|
|
3025
3084
|
else:
|
3026
3085
|
state = tri.match(tri_line, docallback = docallback)
|
3027
3086
|
|
3028
|
-
if state.result == Trigger.SUCCESS:
|
3087
|
+
if state and (state.result == Trigger.SUCCESS):
|
3029
3088
|
triggered_enabled += 1
|
3030
3089
|
if not block:
|
3031
3090
|
triggered += 1
|
@@ -3040,58 +3099,59 @@ class Session:
|
|
3040
3099
|
info_enabled.append(Settings.gettext("msg_tri_ignored", tri.__detailed__(), Settings.WARN_STYLE, Settings.CLR_STYLE))
|
3041
3100
|
# info_enabled.append(f" {Settings.WARN_STYLE}{tri.__detailed__()} 可以触发,但由于优先级与keepEval设定,触发器不会触发。{Settings.CLR_STYLE}")
|
3042
3101
|
|
3043
|
-
|
3044
|
-
|
3045
|
-
|
3046
|
-
|
3047
|
-
|
3048
|
-
|
3049
|
-
|
3050
|
-
|
3051
|
-
|
3052
|
-
|
3053
|
-
|
3102
|
+
if cmd != "echo":
|
3103
|
+
for tri in tris_disabled:
|
3104
|
+
if tri.raw:
|
3105
|
+
state = tri.match(raw_line, docallback = docallback)
|
3106
|
+
else:
|
3107
|
+
state = tri.match(tri_line, docallback = docallback)
|
3108
|
+
|
3109
|
+
if state and (state.result == Trigger.SUCCESS):
|
3110
|
+
triggered_disabled += 1
|
3111
|
+
#info_disabled.append(f" {tri.__detailed__()} 可以匹配触发。")
|
3112
|
+
info_disabled.append(Settings.gettext("msg_tri_matched", tri.__detailed__()))
|
3113
|
+
|
3114
|
+
if triggered_enabled + triggered_disabled == 0:
|
3115
|
+
info_all.append("")
|
3116
|
+
|
3117
|
+
if cmd != "echo":
|
3118
|
+
if triggered_enabled == 0:
|
3119
|
+
info_enabled.insert(0, Settings.gettext("msg_enabled_summary_0", Settings.INFO_STYLE))
|
3120
|
+
#info_enabled.insert(0, f"{Settings.INFO_STYLE} 使能的触发器中,没有可以触发的。")
|
3121
|
+
elif triggered < triggered_enabled:
|
3122
|
+
info_enabled.insert(0, Settings.gettext("msg_enabled_summary_1", Settings.INFO_STYLE, triggered_enabled, triggered, triggered_enabled - triggered))
|
3123
|
+
#info_enabled.insert(0, f"{Settings.INFO_STYLE} 使能的触发器中,共有 {triggered_enabled} 个可以触发,实际触发 {triggered} 个,另有 {triggered_enabled - triggered} 个由于 keepEval 原因实际不会触发。")
|
3124
|
+
else:
|
3125
|
+
info_enabled.insert(0, Settings.gettext("msg_enabled_summary_2", Settings.INFO_STYLE, triggered_enabled))
|
3126
|
+
#info_enabled.insert(0, f"{Settings.INFO_STYLE} 使能的触发器中,共有 {triggered_enabled} 个全部可以被正常触发。")
|
3054
3127
|
|
3128
|
+
if triggered_disabled > 0:
|
3129
|
+
info_disabled.insert(0, Settings.gettext("msg_disabled_summary_0", Settings.INFO_STYLE, triggered_disabled))
|
3130
|
+
#info_disabled.insert(0, f"{Settings.INFO_STYLE} 未使能的触发器中,共有 {triggered_disabled} 个可以匹配。")
|
3131
|
+
else:
|
3132
|
+
info_disabled.insert(0, Settings.gettext("msg_disabled_summary_1", Settings.INFO_STYLE))
|
3133
|
+
#info_disabled.insert(0, f"{Settings.INFO_STYLE} 未使能触发器,没有可以匹配的。")
|
3134
|
+
|
3135
|
+
info_all.append("")
|
3055
3136
|
if triggered_enabled + triggered_disabled == 0:
|
3056
|
-
info_all.append("")
|
3057
|
-
|
3058
|
-
|
3059
|
-
|
3060
|
-
|
3061
|
-
|
3062
|
-
|
3063
|
-
|
3064
|
-
|
3065
|
-
|
3066
|
-
|
3067
|
-
|
3068
|
-
|
3069
|
-
|
3070
|
-
|
3071
|
-
|
3072
|
-
|
3073
|
-
#
|
3074
|
-
|
3075
|
-
info_all.append("")
|
3076
|
-
if triggered_enabled + triggered_disabled == 0:
|
3077
|
-
#info_all.append(f"PYMUD 触发器测试: {'响应模式' if docallback else '测试模式'}")
|
3078
|
-
info_all.append(Settings.gettext("msg_test_summary_0", line))
|
3079
|
-
info_all.append(Settings.gettext("msg_test_summary_1"))
|
3080
|
-
#info_all.append(f" 测试内容: {line}")
|
3081
|
-
#info_all.append(f" 测试结果: 没有可以匹配的触发器。")
|
3082
|
-
else:
|
3083
|
-
#info_all.append(f"PYMUD 触发器测试: {'响应模式' if docallback else '测试模式'}")
|
3084
|
-
info_all.append(Settings.gettext("msg_test_summary_0", line))
|
3085
|
-
info_all.append(Settings.gettext("msg_test_summary_2", triggered, triggered_enabled + triggered_disabled))
|
3086
|
-
#info_all.append(f" 测试内容: {line}")
|
3087
|
-
#info_all.append(f" 测试结果: 有{triggered}个触发器可以被正常触发,一共有{triggered_enabled + triggered_disabled}个满足匹配触发要求。")
|
3088
|
-
info_all.extend(info_enabled)
|
3089
|
-
info_all.extend(info_disabled)
|
3090
|
-
|
3091
|
-
title = Settings.gettext("msg_test_title", Settings.gettext("msg_triggered_mode") if docallback else Settings.gettext("msg_matched_mode"))
|
3092
|
-
#title = f"触发器测试 - {'响应模式' if docallback else '测试模式'}"
|
3093
|
-
self.info("\n".join(info_all), title)
|
3094
|
-
#self.info("PYMUD 触发器测试 完毕")
|
3137
|
+
#info_all.append(f"PYMUD 触发器测试: {'响应模式' if docallback else '测试模式'}")
|
3138
|
+
info_all.append(Settings.gettext("msg_test_summary_0", line))
|
3139
|
+
info_all.append(Settings.gettext("msg_test_summary_1"))
|
3140
|
+
#info_all.append(f" 测试内容: {line}")
|
3141
|
+
#info_all.append(f" 测试结果: 没有可以匹配的触发器。")
|
3142
|
+
else:
|
3143
|
+
#info_all.append(f"PYMUD 触发器测试: {'响应模式' if docallback else '测试模式'}")
|
3144
|
+
info_all.append(Settings.gettext("msg_test_summary_0", line))
|
3145
|
+
info_all.append(Settings.gettext("msg_test_summary_2", triggered, triggered_enabled + triggered_disabled))
|
3146
|
+
#info_all.append(f" 测试内容: {line}")
|
3147
|
+
#info_all.append(f" 测试结果: 有{triggered}个触发器可以被正常触发,一共有{triggered_enabled + triggered_disabled}个满足匹配触发要求。")
|
3148
|
+
info_all.extend(info_enabled)
|
3149
|
+
info_all.extend(info_disabled)
|
3150
|
+
|
3151
|
+
title = Settings.gettext("msg_test_title", Settings.gettext("msg_triggered_mode") if docallback else Settings.gettext("msg_matched_mode"))
|
3152
|
+
#title = f"触发器测试 - {'响应模式' if docallback else '测试模式'}"
|
3153
|
+
self.info("\n".join(info_all), title)
|
3154
|
+
#self.info("PYMUD 触发器测试 完毕")
|
3095
3155
|
|
3096
3156
|
def handle_plugins(self, code: CodeLine, *args, **kwargs):
|
3097
3157
|
'''
|
@@ -3152,7 +3212,7 @@ class Session:
|
|
3152
3212
|
new_text, new_code = code.expand(self, *args, **kwargs)
|
3153
3213
|
self.replace(new_text[9:])
|
3154
3214
|
|
3155
|
-
def handle_gag(self, code: CodeLine, *args, **kwargs):
|
3215
|
+
def handle_gag(self, code: Optional[CodeLine] = None, *args, **kwargs):
|
3156
3216
|
'''
|
3157
3217
|
嵌入命令 #gag 的执行函数,在主窗口中不显示当前行内容,一般用于触发器中。
|
3158
3218
|
该函数不应该在代码中直接调用。
|
@@ -3239,7 +3299,6 @@ class Session:
|
|
3239
3299
|
def info2(self, msg, title = None, style = Settings.INFO_STYLE):
|
3240
3300
|
title = title or Settings.gettext("title_msg")
|
3241
3301
|
msg = f"{msg}"
|
3242
|
-
|
3243
3302
|
self.writetobuffer("{}〔{}〕{}{}".format(style, title, msg, Settings.CLR_STYLE), newline = True)
|
3244
3303
|
|
3245
3304
|
def info(self, msg, title = None, style = Settings.INFO_STYLE):
|
@@ -3387,34 +3446,3 @@ class Session:
|
|
3387
3446
|
else:
|
3388
3447
|
self.application.show_logSelectDialog()
|
3389
3448
|
|
3390
|
-
|
3391
|
-
def exception(func):
|
3392
|
-
"""方法异常处理装饰器,捕获异常后通过会话的session.error打印相关信息"""
|
3393
|
-
@functools.wraps(func)
|
3394
|
-
def wrapper(self, *args, **kwargs):
|
3395
|
-
try:
|
3396
|
-
return func(self, *args, **kwargs)
|
3397
|
-
except Exception as e:
|
3398
|
-
# 调用类的错误处理方法
|
3399
|
-
session = getattr(self, "session", None)
|
3400
|
-
if isinstance(session, Session):
|
3401
|
-
session.error(f"函数执行中遇到异常, {e}, 类型为 {type(e)}")
|
3402
|
-
session.error(f"异常追踪为: {traceback.format_exc()}")
|
3403
|
-
else:
|
3404
|
-
raise # 当没有会话时,选择重新抛出异常
|
3405
|
-
return wrapper
|
3406
|
-
|
3407
|
-
def async_exception(func):
|
3408
|
-
"""异步方法异常处理装饰器,捕获异常后通过会话的session.error打印相关信息"""
|
3409
|
-
@functools.wraps(func)
|
3410
|
-
async def wrapper(self, *args, **kwargs):
|
3411
|
-
try:
|
3412
|
-
return await func(self, *args, **kwargs)
|
3413
|
-
except Exception as e:
|
3414
|
-
session = getattr(self, "session", None)
|
3415
|
-
if isinstance(session, Session):
|
3416
|
-
session.error(f"异步执行中遇到异常, {e}, 类型为 {type(e)}")
|
3417
|
-
session.error(f"异常追踪为: {traceback.format_exc()}")
|
3418
|
-
else:
|
3419
|
-
raise # 当没有会话时,选择重新抛出异常
|
3420
|
-
return wrapper
|
pymud/settings.py
CHANGED
@@ -15,7 +15,7 @@ class Settings:
|
|
15
15
|
"APP 简要描述"
|
16
16
|
__version__ = importlib.metadata.version("pymud")
|
17
17
|
"APP 当前版本"
|
18
|
-
__release__ = "2025-05-
|
18
|
+
__release__ = "2025-05-20"
|
19
19
|
"APP 当前版本发布日期"
|
20
20
|
__author__ = "本牛(newstart)@北侠"
|
21
21
|
"APP 作者"
|
@@ -35,7 +35,7 @@ class Settings:
|
|
35
35
|
"SGA" : True, # Supress Go Ahead
|
36
36
|
"ECHO" : False, # Echo
|
37
37
|
"GMCP" : True, # Generic Mud Communication Protocol
|
38
|
-
"MSDP" :
|
38
|
+
"MSDP" : True, # Mud Server Data Protocol
|
39
39
|
"MSSP" : True, # Mud Server Status Protocol
|
40
40
|
"MCCP2" : False, # Mud Compress Communication Protocol V2
|
41
41
|
"MCCP3" : False, # Mud Compress Communication Protocol V3
|