pymud 0.19.3.post2__py3-none-any.whl → 0.20.0__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 -1
- pymud/__main__.py +2 -100
- pymud/dialogs.py +33 -1
- pymud/extras.py +2 -72
- pymud/logger.py +160 -0
- pymud/main.py +142 -0
- pymud/modules.py +188 -0
- pymud/objects.py +51 -20
- pymud/pkuxkx.py +4 -7
- pymud/pymud.py +233 -73
- pymud/session.py +532 -141
- pymud/settings.py +4 -3
- {pymud-0.19.3.post2.dist-info → pymud-0.20.0.dist-info}/METADATA +225 -181
- pymud-0.20.0.dist-info/RECORD +19 -0
- {pymud-0.19.3.post2.dist-info → pymud-0.20.0.dist-info}/WHEEL +1 -1
- pymud-0.19.3.post2.dist-info/RECORD +0 -16
- {pymud-0.19.3.post2.dist-info → pymud-0.20.0.dist-info}/LICENSE.txt +0 -0
- {pymud-0.19.3.post2.dist-info → pymud-0.20.0.dist-info}/entry_points.txt +0 -0
- {pymud-0.19.3.post2.dist-info → pymud-0.20.0.dist-info}/top_level.txt +0 -0
pymud/session.py
CHANGED
@@ -1,10 +1,15 @@
|
|
1
|
-
import asyncio, logging, re, math, os, pickle, datetime, importlib, importlib.util, sysconfig
|
1
|
+
import asyncio, logging, re, math, os, pickle, datetime, importlib, importlib.util, sysconfig, time
|
2
2
|
from collections.abc import Iterable
|
3
3
|
from collections import OrderedDict
|
4
|
-
|
5
|
-
from
|
4
|
+
import logging, queue
|
5
|
+
from logging import FileHandler
|
6
|
+
from logging.handlers import QueueHandler, QueueListener
|
7
|
+
from wcwidth import wcswidth, wcwidth
|
8
|
+
from .logger import Logger
|
9
|
+
from .extras import SessionBuffer, DotDict
|
6
10
|
from .protocol import MudClientProtocol
|
7
|
-
from .
|
11
|
+
from .modules import ModuleInfo
|
12
|
+
from .objects import BaseObject, Trigger, Alias, Command, Timer, SimpleAlias, SimpleTrigger, SimpleTimer, GMCPTrigger, CodeBlock, CodeLine
|
8
13
|
from .settings import Settings
|
9
14
|
|
10
15
|
|
@@ -25,13 +30,14 @@ class Session:
|
|
25
30
|
|
26
31
|
"""
|
27
32
|
#_esc_regx = re.compile("\x1b\\[[^mz]+[mz]")
|
28
|
-
_esc_regx = re.compile("\x1b
|
33
|
+
_esc_regx = re.compile(r"\x1b\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
|
29
34
|
|
30
35
|
_sys_commands = (
|
31
36
|
"help",
|
32
37
|
"exit",
|
33
38
|
"close",
|
34
39
|
"connect", # 连接到服务器
|
40
|
+
"disconnect", # 从服务器断开连接
|
35
41
|
|
36
42
|
"info", # 输出蓝色info
|
37
43
|
"warning", # 输出黄色warning
|
@@ -73,6 +79,8 @@ class Session:
|
|
73
79
|
"py", # 直接执行python语句
|
74
80
|
|
75
81
|
"all", # 所有会话执行
|
82
|
+
|
83
|
+
"log", # 记录处置
|
76
84
|
)
|
77
85
|
|
78
86
|
_commands_alias = {
|
@@ -83,6 +91,7 @@ class Session:
|
|
83
91
|
"var" : "variable",
|
84
92
|
"rep" : "repeat",
|
85
93
|
"con" : "connect",
|
94
|
+
"dis" : "disconnect",
|
86
95
|
"wa" : "wait",
|
87
96
|
"mess": "message",
|
88
97
|
"action": "trigger",
|
@@ -96,7 +105,8 @@ class Session:
|
|
96
105
|
def __init__(self, app, name, host, port, encoding = None, after_connect = None, loop = None, **kwargs):
|
97
106
|
self.pyversion = sysconfig.get_python_version()
|
98
107
|
self.loop = loop or asyncio.get_running_loop()
|
99
|
-
self.
|
108
|
+
self.syslog = logging.getLogger("pymud.Session")
|
109
|
+
|
100
110
|
self.application = app
|
101
111
|
self.name = name
|
102
112
|
self._transport = None
|
@@ -132,8 +142,13 @@ class Session:
|
|
132
142
|
self._status_maker = None # 创建状态窗口的函数(属性)
|
133
143
|
self.display_line = ""
|
134
144
|
|
145
|
+
self._activetime = time.time()
|
146
|
+
|
135
147
|
self.initialize()
|
136
148
|
|
149
|
+
self._loggers = dict()
|
150
|
+
self.log = self.getLogger(name)
|
151
|
+
|
137
152
|
self.host = host
|
138
153
|
self.port = port
|
139
154
|
self.encoding = encoding or self.encoding
|
@@ -159,7 +174,6 @@ class Session:
|
|
159
174
|
self.warning(f"自动从{file}中加载变量失败,错误消息为: {e}")
|
160
175
|
|
161
176
|
|
162
|
-
|
163
177
|
if self._auto_script:
|
164
178
|
self.info(f"即将自动加载以下模块:{self._auto_script}")
|
165
179
|
self.load_module(self._auto_script)
|
@@ -167,6 +181,10 @@ class Session:
|
|
167
181
|
if Settings.client["auto_connect"]:
|
168
182
|
self.open()
|
169
183
|
|
184
|
+
def __del__(self):
|
185
|
+
self.clean()
|
186
|
+
self.closeLoggers()
|
187
|
+
|
170
188
|
def initialize(self):
|
171
189
|
"初始化Session有关对象。 **无需脚本调用。**"
|
172
190
|
self._line_buffer = bytearray()
|
@@ -239,11 +257,6 @@ class Session:
|
|
239
257
|
if self.connected:
|
240
258
|
self.write_eof()
|
241
259
|
|
242
|
-
# 两次保存,删掉一次
|
243
|
-
# # 断开时自动保存变量数据
|
244
|
-
# if Settings.client["var_autosave"]:
|
245
|
-
# self.handle_save()
|
246
|
-
|
247
260
|
def onDisconnected(self, protocol):
|
248
261
|
"当从服务器连接断开时执行的操作。包括保存变量(若设置)、打印断开时间、执行自定义事件(若设置)等。"
|
249
262
|
# 断开时自动保存变量数据
|
@@ -281,6 +294,15 @@ class Session:
|
|
281
294
|
|
282
295
|
return dura
|
283
296
|
|
297
|
+
@property
|
298
|
+
def idletime(self) -> float:
|
299
|
+
"只读属性,返回当前会话空闲时间(即最后一次向服务器写入数据到现在的时间),以秒为单位。当服务器未连接时,返回-1"
|
300
|
+
idle = -1
|
301
|
+
if self._protocol and self._protocol.connected:
|
302
|
+
idle = time.time() - self._activetime
|
303
|
+
|
304
|
+
return idle
|
305
|
+
|
284
306
|
@property
|
285
307
|
def status_maker(self):
|
286
308
|
"""
|
@@ -340,6 +362,42 @@ class Session:
|
|
340
362
|
def event_disconnected(self, event):
|
341
363
|
self._events["disconnected"] = event
|
342
364
|
|
365
|
+
def getLogger(self, name, mode = 'a', encoding = 'utf-8', encoding_errors = 'ignore', raw = False) -> Logger:
|
366
|
+
"""
|
367
|
+
根据指定名称和参数获取并返回一个记录器。若指定名称不存在,则创建一个该名称记录器。
|
368
|
+
|
369
|
+
:param name: 指定的记录器名称
|
370
|
+
:param mode: 记录器的模式,可接受值为 a, w, n。 具体请参见 Logger 对象中 mode 参数
|
371
|
+
:param encoding: 记录文件的编码格式
|
372
|
+
:param encoding_errors: 编码错误的处理方式
|
373
|
+
:param raw: 是否以带ANSI标记的原始格式进行记录
|
374
|
+
|
375
|
+
:return 指定名称的记录器 Logger 对象
|
376
|
+
"""
|
377
|
+
if name not in self.application.loggers.keys():
|
378
|
+
logger = Logger(name, mode, encoding, encoding_errors, raw)
|
379
|
+
self._loggers[name] = logger
|
380
|
+
self.application.loggers[name] = logger
|
381
|
+
|
382
|
+
else:
|
383
|
+
if name not in self._loggers.keys():
|
384
|
+
self.warning(f"其它会话中已存在一个名为 {name} 的记录器,将直接返回该记录器")
|
385
|
+
|
386
|
+
logger = self.application.loggers[name]
|
387
|
+
logger.mode = mode
|
388
|
+
logger.raw = raw
|
389
|
+
|
390
|
+
return logger
|
391
|
+
|
392
|
+
def closeLoggers(self):
|
393
|
+
"移除本会话所有相关Logger"
|
394
|
+
for name in self._loggers.keys():
|
395
|
+
if isinstance(self._loggers[name], Logger):
|
396
|
+
self._loggers[name].enabled = False
|
397
|
+
|
398
|
+
if name in self.application.loggers.keys():
|
399
|
+
self.application.loggers.pop(name)
|
400
|
+
|
343
401
|
@property
|
344
402
|
def modules(self) -> OrderedDict:
|
345
403
|
"""
|
@@ -484,6 +542,7 @@ class Session:
|
|
484
542
|
:param newline: 是否额外增加换行符
|
485
543
|
"""
|
486
544
|
self.buffer.insert_text(data)
|
545
|
+
self.log.log(data)
|
487
546
|
|
488
547
|
if len(data) > 0 and (data[-1] == "\n"):
|
489
548
|
self._line_count += 1
|
@@ -491,6 +550,7 @@ class Session:
|
|
491
550
|
if newline:
|
492
551
|
self.buffer.insert_text(self.newline_cli)
|
493
552
|
self._line_count += 1
|
553
|
+
self.log.log(self.newline_cli)
|
494
554
|
|
495
555
|
def clear_half(self):
|
496
556
|
"""
|
@@ -520,7 +580,7 @@ class Session:
|
|
520
580
|
if self.connected:
|
521
581
|
self._transport.write_eof()
|
522
582
|
self.state = "DISCONNECTED"
|
523
|
-
self.
|
583
|
+
self.syslog.info(f"服务器断开连接! {self._protocol.__repr__}")
|
524
584
|
|
525
585
|
def feed_gmcp(self, name, value) -> None:
|
526
586
|
"""
|
@@ -703,22 +763,48 @@ class Session:
|
|
703
763
|
if self.seperator in line:
|
704
764
|
lines = line.split(self.seperator)
|
705
765
|
for ln in lines:
|
766
|
+
if Settings.client["echo_input"]:
|
767
|
+
self.writetobuffer(f"\x1b[32m{ln}\x1b[0m", True)
|
768
|
+
else:
|
769
|
+
self.log.log(f"\x1b[32m{ln}\x1b[0m\n")
|
770
|
+
|
706
771
|
cmd = ln + self.newline
|
707
772
|
self.write(cmd.encode(self.encoding, Settings.server["encoding_errors"]))
|
708
773
|
|
709
|
-
if Settings.client["echo_input"]:
|
710
|
-
self.writetobuffer(f"\x1b[32m{cmd}\x1b[0m")
|
711
|
-
|
712
774
|
else:
|
775
|
+
if Settings.client["echo_input"]:
|
776
|
+
self.writetobuffer(f"\x1b[32m{line}\x1b[0m", True)
|
777
|
+
else:
|
778
|
+
self.log.log(f"\x1b[32m{line}\x1b[0m\n")
|
779
|
+
|
713
780
|
cmd = line + self.newline
|
714
781
|
self.write(cmd.encode(self.encoding, Settings.server["encoding_errors"]))
|
715
782
|
|
716
|
-
|
717
|
-
if Settings.client["echo_input"] and (len(cmd) > len(self.newline)): # 修改2023-12-3, 向服务器发送空回车时,不回显
|
718
|
-
self.writetobuffer(f"\x1b[32m{cmd}\x1b[0m")
|
783
|
+
self._activetime = time.time()
|
719
784
|
|
720
|
-
def
|
785
|
+
async def waitfor(self, line: str, awaitable, wait_time = 0.05) -> None:
|
786
|
+
"""
|
787
|
+
调用writline向服务器中写入一行后,等待到可等待对象再返回。
|
788
|
+
|
789
|
+
:param line: 使用writeline写入的行
|
790
|
+
:param awaitable: 等待的可等待对象
|
791
|
+
:param wait_time: 写入行前等待的延时,单位为s。默认0.05
|
792
|
+
|
793
|
+
由于异步的消息循环机制,如果在写入命令之后再创建可等待对象,则有可能服务器响应在可等待对象的创建之前
|
794
|
+
此时使用await就无法等待到可等待对象的响应,会导致任务出错。
|
795
|
+
一种解决方式是先创建可等待对象,然后写入命令,然后再等待可等待对象,但这种情况下需要写入三行代码,书写麻烦
|
796
|
+
因此该函数是用于简化此类使用时的写法。
|
797
|
+
|
798
|
+
示例:
|
799
|
+
await session.waitfor('a_cmd', self.create_task(a_tri.triggered()))
|
800
|
+
done, pending = await session.waitfor('a_cmd', asyncio.wait([self.create_task(a_tri.triggered()), self.create_task(b_tri.triggered())], return_when = 'FIRST_COMPLETED'))
|
721
801
|
"""
|
802
|
+
await asyncio.sleep(wait_time)
|
803
|
+
self.writeline(line)
|
804
|
+
return await awaitable
|
805
|
+
|
806
|
+
def exec(self, cmd: str, name = None, *args, **kwargs):
|
807
|
+
r"""
|
722
808
|
在名称为name的会话中使用exec_command执行MUD命令。当不指定name时,在当前会话中执行。
|
723
809
|
|
724
810
|
- exec 与 writeline 都会向服务器写入数据。其差异在于,exec执行的内容,会先经过Alias处理和Command处理,实际向远程发送内容与cmd可以不一致。
|
@@ -733,7 +819,7 @@ class Session:
|
|
733
819
|
示例:
|
734
820
|
.. code:: Python
|
735
821
|
|
736
|
-
session.addAlias(SimpleAlias(self.session, "^cb\s(\S+)\s(\S+)", "#3 get %1 from jinnang;#wa 250;combine gem;#wa 250;pack gem", id = "ali_combine"))
|
822
|
+
session.addAlias(SimpleAlias(self.session, r"^cb\s(\S+)\s(\S+)", "#3 get %1 from jinnang;#wa 250;combine gem;#wa 250;pack gem", id = "ali_combine"))
|
737
823
|
session.exec("cb j1a")
|
738
824
|
"""
|
739
825
|
name = name or self.name
|
@@ -754,12 +840,10 @@ class Session:
|
|
754
840
|
name = name or self.name
|
755
841
|
if name in self.application.sessions.keys():
|
756
842
|
session = self.application.sessions[name]
|
757
|
-
await session.exec_command_async(cmd, *args, **kwargs)
|
843
|
+
return await session.exec_command_async(cmd, *args, **kwargs)
|
758
844
|
else:
|
759
845
|
self.error(f"不存在名称为{name}的会话")
|
760
846
|
|
761
|
-
|
762
|
-
|
763
847
|
def exec_code(self, cl: CodeLine, *args, **kwargs):
|
764
848
|
"""
|
765
849
|
执行解析为CodeLine形式的MUD命令(必定为单个命令)。一般情况下,脚本中不应调用该方法,而应使用exec/exec_command。
|
@@ -833,6 +917,7 @@ class Session:
|
|
833
917
|
:param args: 保留兼容与扩展性所需
|
834
918
|
:param kwargs: 保留兼容与扩展性所需
|
835
919
|
"""
|
920
|
+
|
836
921
|
if cl.length == 0:
|
837
922
|
self.writeline("")
|
838
923
|
|
@@ -860,9 +945,9 @@ class Session:
|
|
860
945
|
else:
|
861
946
|
try:
|
862
947
|
cb = CodeBlock(sess_cmd)
|
863
|
-
await cb.async_execute(session, *args, **kwargs)
|
948
|
+
return await cb.async_execute(session, *args, **kwargs)
|
864
949
|
except Exception as e:
|
865
|
-
await session.exec_command_async(sess_cmd)
|
950
|
+
return await session.exec_command_async(sess_cmd)
|
866
951
|
|
867
952
|
else:
|
868
953
|
if cmd in self._commands_alias.keys():
|
@@ -879,7 +964,7 @@ class Session:
|
|
879
964
|
|
880
965
|
else:
|
881
966
|
cmdtext, code = cl.expand(self, *args, **kwargs)
|
882
|
-
await self.exec_text_async(cmdtext)
|
967
|
+
return await self.exec_text_async(cmdtext)
|
883
968
|
|
884
969
|
def exec_text(self, cmdtext: str):
|
885
970
|
"""
|
@@ -919,14 +1004,14 @@ class Session:
|
|
919
1004
|
|
920
1005
|
异步调用时,该函数要等待对应的代码执行完毕后才会返回。可以用于确保命令执行完毕。
|
921
1006
|
"""
|
922
|
-
|
1007
|
+
result = None
|
923
1008
|
isNotCmd = True
|
924
1009
|
for command in self._commands.values():
|
925
1010
|
if isinstance(command, Command) and command.enabled:
|
926
1011
|
state = command.match(cmdtext)
|
927
1012
|
if state.result == Command.SUCCESS:
|
928
1013
|
# 命令的任务名称采用命令id,以便于后续查错
|
929
|
-
await self.create_task(command.execute(cmdtext), name = "task-{0}".format(command.id))
|
1014
|
+
result = await self.create_task(command.execute(cmdtext), name = "task-{0}".format(command.id))
|
930
1015
|
isNotCmd = False
|
931
1016
|
break
|
932
1017
|
|
@@ -944,6 +1029,8 @@ class Session:
|
|
944
1029
|
if notAlias:
|
945
1030
|
self.writeline(cmdtext)
|
946
1031
|
|
1032
|
+
return result
|
1033
|
+
|
947
1034
|
def exec_command(self, line: str, *args, **kwargs) -> None:
|
948
1035
|
"""
|
949
1036
|
在当前会话中执行MUD命令。多个命令可以用分隔符隔开。
|
@@ -994,15 +1081,18 @@ class Session:
|
|
994
1081
|
"""
|
995
1082
|
|
996
1083
|
## 以下为函数执行本体
|
1084
|
+
result = None
|
997
1085
|
if (not "#" in line) and (not "@" in line) and (not "%" in line):
|
998
1086
|
cmds = line.split(self.seperator)
|
999
1087
|
for cmd in cmds:
|
1000
|
-
await self.exec_text_async(cmd)
|
1088
|
+
result = await self.exec_text_async(cmd)
|
1001
1089
|
if Settings.client["interval"] > 0:
|
1002
1090
|
await asyncio.sleep(Settings.client["interval"] / 1000.0)
|
1003
1091
|
else:
|
1004
1092
|
cb = CodeBlock(line)
|
1005
|
-
await cb.async_execute(self)
|
1093
|
+
result = await cb.async_execute(self)
|
1094
|
+
|
1095
|
+
return result
|
1006
1096
|
|
1007
1097
|
def write_eof(self) -> None:
|
1008
1098
|
"""
|
@@ -1066,50 +1156,212 @@ class Session:
|
|
1066
1156
|
|
1067
1157
|
return counts
|
1068
1158
|
|
1069
|
-
def _addObjects(self, objs: dict, cls: type):
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
def
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1159
|
+
# def _addObjects(self, objs: dict, cls: type):
|
1160
|
+
# if cls == Alias:
|
1161
|
+
# self._aliases.update(objs)
|
1162
|
+
# elif cls == Command:
|
1163
|
+
# self._commands.update(objs)
|
1164
|
+
# elif cls == Trigger:
|
1165
|
+
# self._triggers.update(objs)
|
1166
|
+
# elif cls == Timer:
|
1167
|
+
# self._timers.update(objs)
|
1168
|
+
# elif cls == GMCPTrigger:
|
1169
|
+
# self._gmcp.update(objs)
|
1170
|
+
|
1171
|
+
def _addObjects(self, objs):
|
1172
|
+
if isinstance(objs, list) or isinstance(objs, tuple):
|
1173
|
+
for item in objs:
|
1174
|
+
self._addObject(item)
|
1175
|
+
|
1176
|
+
elif isinstance(objs, dict):
|
1177
|
+
for key, item in objs.items():
|
1178
|
+
if isinstance(item, BaseObject):
|
1179
|
+
if key != item.id:
|
1180
|
+
self.warning(f'对象 {item} 字典键值 {key} 与其id {item.id} 不一致,将丢弃键值,以其id添加到会话中...')
|
1181
|
+
|
1182
|
+
self._addObject(item)
|
1183
|
+
|
1184
|
+
# def _addObject(self, obj, cls: type):
|
1185
|
+
# #if type(obj) == cls:
|
1186
|
+
# if isinstance(obj, cls):
|
1187
|
+
# if cls == Alias:
|
1188
|
+
# self._aliases[obj.id] = obj
|
1189
|
+
# elif cls == Command:
|
1190
|
+
# self._commands[obj.id] = obj
|
1191
|
+
# elif cls == Trigger:
|
1192
|
+
# self._triggers[obj.id] = obj
|
1193
|
+
# elif cls == Timer:
|
1194
|
+
# self._timers[obj.id] = obj
|
1195
|
+
# elif cls == GMCPTrigger:
|
1196
|
+
# self._gmcp[obj.id] = obj
|
1197
|
+
|
1198
|
+
def _addObject(self, obj):
|
1199
|
+
if isinstance(obj, Alias):
|
1200
|
+
self._aliases[obj.id] = obj
|
1201
|
+
elif isinstance(obj, Command):
|
1202
|
+
self._commands[obj.id] = obj
|
1203
|
+
elif isinstance(obj, Trigger):
|
1204
|
+
self._triggers[obj.id] = obj
|
1205
|
+
elif isinstance(obj, Timer):
|
1206
|
+
self._timers[obj.id] = obj
|
1207
|
+
elif isinstance(obj, GMCPTrigger):
|
1208
|
+
self._gmcp[obj.id] = obj
|
1209
|
+
|
1210
|
+
def addObject(self, obj: BaseObject):
|
1211
|
+
"""
|
1212
|
+
向会话中增加单个对象,可直接添加 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类
|
1213
|
+
|
1214
|
+
:param obj: 特定对象本身,可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或其子类
|
1215
|
+
|
1216
|
+
示例:
|
1217
|
+
.. code:: Python
|
1218
|
+
|
1219
|
+
class Configuration:
|
1220
|
+
def __init__(self, session):
|
1221
|
+
self.session = session
|
1222
|
+
|
1223
|
+
self.session.addObject(SimpleAlias(session, r'^gta$', 'get all'),)
|
1224
|
+
self.session.addObject(SimpleTrigger(session, r'^[> ]*你嘻嘻地笑了起来.+', 'haha'))
|
1225
|
+
self.session.addObject(SimpleTimer(session, 'xixi', timeout = 10))
|
1226
|
+
|
1227
|
+
"""
|
1228
|
+
self._addObject(obj)
|
1229
|
+
|
1230
|
+
def addObjects(self, objs):
|
1231
|
+
"""
|
1232
|
+
向会话中增加多个对象,可直接添加 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类的元组、列表或者字典(保持兼容性)
|
1233
|
+
|
1234
|
+
:param objs: 多个特定对象组成的元组、列表或者字典,可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或其子类
|
1235
|
+
|
1236
|
+
示例:
|
1237
|
+
.. code:: Python
|
1238
|
+
|
1239
|
+
class Configuration:
|
1240
|
+
def __init__(self, session):
|
1241
|
+
self.session = session
|
1242
|
+
|
1243
|
+
self.objs = [
|
1244
|
+
SimpleAlias(session, r'^gta$', 'get all;xixi'),
|
1245
|
+
SimpleTrigger(session, r'^[> ]*你嘻嘻地笑了起来.+', 'haha'),
|
1246
|
+
SimpleTimer(session, 'xixi', timeout = 10)
|
1247
|
+
]
|
1248
|
+
|
1249
|
+
self.session.addObjects(self.objs)
|
1250
|
+
|
1251
|
+
"""
|
1252
|
+
self._addObjects(objs)
|
1094
1253
|
|
1095
1254
|
def _delObject(self, id, cls: type):
|
1096
1255
|
if cls == Alias:
|
1097
1256
|
self._aliases.pop(id, None)
|
1098
1257
|
elif cls == Command:
|
1099
|
-
self._commands.pop(id, None)
|
1258
|
+
cmd = self._commands.pop(id, None)
|
1259
|
+
if isinstance(cmd, Command):
|
1260
|
+
cmd.reset()
|
1261
|
+
cmd.unload()
|
1262
|
+
cmd.__unload__()
|
1263
|
+
|
1100
1264
|
elif cls == Trigger:
|
1101
1265
|
self._triggers.pop(id, None)
|
1102
1266
|
elif cls == Timer:
|
1103
|
-
self._timers.pop(id, None)
|
1267
|
+
timer = self._timers.pop(id, None)
|
1268
|
+
if isinstance(timer, Timer):
|
1269
|
+
timer.enabled = False
|
1104
1270
|
elif cls == GMCPTrigger:
|
1105
1271
|
self._gmcp.pop(id, None)
|
1106
1272
|
|
1273
|
+
|
1107
1274
|
def _delObjects(self, ids: Iterable, cls: type):
|
1108
1275
|
"删除多个指定元素"
|
1109
1276
|
for id in ids:
|
1110
1277
|
self._delObject(id, cls)
|
1111
1278
|
|
1112
|
-
def
|
1279
|
+
def delObject(self, obj):
|
1280
|
+
"""
|
1281
|
+
从会话中移除一个对象,可直接删除 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类本身
|
1282
|
+
|
1283
|
+
** 注 ** 现在 delObject 和 delObjects 使用结果相同,都可以清除单个对象、对个对象的list, tuple或dict, 可以有效防止代码写错
|
1284
|
+
|
1285
|
+
:param obj: 要删除的多个特定对象组成的元组、列表或者字典,可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或其子类
|
1286
|
+
|
1287
|
+
示例:
|
1288
|
+
.. code:: Python
|
1289
|
+
|
1290
|
+
class Configuration:
|
1291
|
+
def __init__(self, session):
|
1292
|
+
self.session = session
|
1293
|
+
|
1294
|
+
ali = SimpleAlias(session, r'^gta$', 'get all', id = 'my_ali1')
|
1295
|
+
|
1296
|
+
# 以下几种方式均可将该别名添加到会话
|
1297
|
+
session.addObject(ali)
|
1298
|
+
session.addAlias(ali)
|
1299
|
+
|
1300
|
+
# 以下三种方式均可以删除该别名
|
1301
|
+
session.delObject(ali)
|
1302
|
+
session.delAlias(ali)
|
1303
|
+
session.delAlias("my_ali1")
|
1304
|
+
|
1305
|
+
"""
|
1306
|
+
if isinstance(obj, Alias):
|
1307
|
+
self._aliases.pop(obj.id, None)
|
1308
|
+
elif isinstance(obj, Command):
|
1309
|
+
obj.reset()
|
1310
|
+
obj.unload()
|
1311
|
+
obj.__unload__()
|
1312
|
+
self._commands.pop(obj.id, None)
|
1313
|
+
elif isinstance(obj, Trigger):
|
1314
|
+
self._triggers.pop(obj.id, None)
|
1315
|
+
elif isinstance(obj, Timer):
|
1316
|
+
obj.enabled = False
|
1317
|
+
self._timers.pop(obj.id, None)
|
1318
|
+
elif isinstance(obj, GMCPTrigger):
|
1319
|
+
self._gmcp.pop(obj.id, None)
|
1320
|
+
|
1321
|
+
elif isinstance(obj, (list, tuple, dict)):
|
1322
|
+
self.delObjects(obj)
|
1323
|
+
|
1324
|
+
def delObjects(self, objs):
|
1325
|
+
"""
|
1326
|
+
从会话中移除一组对象,可直接删除多个 Alias, Trigger, GMCPTrigger, Command, Timer
|
1327
|
+
|
1328
|
+
** 注 ** 现在 delObject 和 delObjects 使用结果相同,都可以清除单个对象、对个对象的list, tuple或dict, 可以有效防止代码写错
|
1329
|
+
|
1330
|
+
:param objs: 要删除的一组对象的元组、列表或者字典(保持兼容性),其中对象可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类
|
1331
|
+
|
1332
|
+
示例:
|
1333
|
+
|
1334
|
+
.. code:: Python
|
1335
|
+
|
1336
|
+
class Configuration:
|
1337
|
+
def __init__(self, session):
|
1338
|
+
self.session = session
|
1339
|
+
|
1340
|
+
self.objs = [
|
1341
|
+
SimpleAlias(session, r'^gta$', 'get all;xixi'),
|
1342
|
+
SimpleTrigger(session, r'^[> ]*你嘻嘻地笑了起来.+', 'haha'),
|
1343
|
+
SimpleTimer(session, 'xixi', timeout = 10)
|
1344
|
+
]
|
1345
|
+
|
1346
|
+
self.session.addObjects(self.objs)
|
1347
|
+
|
1348
|
+
def __unload__(self):
|
1349
|
+
"卸载本模块时,删除所有本模块添加的对象"
|
1350
|
+
self.session.delObjects(self.objs)
|
1351
|
+
|
1352
|
+
"""
|
1353
|
+
if isinstance(objs, list) or isinstance(objs, tuple):
|
1354
|
+
for item in objs:
|
1355
|
+
self.delObject(item)
|
1356
|
+
|
1357
|
+
elif isinstance(objs, dict):
|
1358
|
+
for key, item in objs.items():
|
1359
|
+
self.delObject(item)
|
1360
|
+
|
1361
|
+
elif isinstance(objs, BaseObject):
|
1362
|
+
self.delObject(objs)
|
1363
|
+
|
1364
|
+
def addAliases(self, alis):
|
1113
1365
|
"""
|
1114
1366
|
向会话中增加多个别名
|
1115
1367
|
|
@@ -1130,55 +1382,55 @@ class Session:
|
|
1130
1382
|
self._aliases['my_ali2'] = SimpleAlias(self.session, "s", "south", id = "my_ali2")
|
1131
1383
|
self.session.addAliases(self._aliases)
|
1132
1384
|
"""
|
1133
|
-
self._addObjects(alis
|
1385
|
+
self._addObjects(alis)
|
1134
1386
|
|
1135
|
-
def addCommands(self, cmds
|
1387
|
+
def addCommands(self, cmds):
|
1136
1388
|
"""
|
1137
1389
|
向会话中增加多个命令。使用方法与 addAliases 类似。
|
1138
1390
|
|
1139
1391
|
:param cmds: 多个命令的字典。字典 key 应为每个命令的 id。
|
1140
1392
|
"""
|
1141
|
-
self._addObjects(cmds
|
1393
|
+
self._addObjects(cmds)
|
1142
1394
|
|
1143
|
-
def addTriggers(self, tris
|
1395
|
+
def addTriggers(self, tris):
|
1144
1396
|
"""
|
1145
1397
|
向会话中增加多个触发器。使用方法与 addAliases 类似。
|
1146
1398
|
|
1147
1399
|
:param tris: 多个触发器的字典。字典 key 应为每个触发器的 id。
|
1148
1400
|
"""
|
1149
|
-
self._addObjects(tris
|
1401
|
+
self._addObjects(tris)
|
1150
1402
|
|
1151
|
-
def addGMCPs(self, gmcps
|
1403
|
+
def addGMCPs(self, gmcps):
|
1152
1404
|
"""
|
1153
1405
|
向会话中增加多个GMCPTrigger。使用方法与 addAliases 类似。
|
1154
1406
|
|
1155
1407
|
:param gmcps: 多个GMCPTrigger的字典。字典 key 应为每个GMCPTrigger的 id。
|
1156
1408
|
"""
|
1157
|
-
self._addObjects(gmcps
|
1409
|
+
self._addObjects(gmcps)
|
1158
1410
|
|
1159
|
-
def addTimers(self, tis
|
1411
|
+
def addTimers(self, tis):
|
1160
1412
|
"""
|
1161
1413
|
向会话中增加多个定时器。使用方法与 addAliases 类似。
|
1162
1414
|
|
1163
1415
|
:param tis: 多个定时器的字典。字典 key 应为每个定时器的 id。
|
1164
1416
|
"""
|
1165
|
-
self._addObjects(tis
|
1417
|
+
self._addObjects(tis)
|
1166
1418
|
|
1167
|
-
def addAlias(self, ali
|
1419
|
+
def addAlias(self, ali):
|
1168
1420
|
"""
|
1169
1421
|
向会话中增加一个别名。
|
1170
1422
|
|
1171
1423
|
:param ali: 要增加的别名对象,应为 Alias 类型或其子类
|
1172
1424
|
"""
|
1173
|
-
self._addObject(ali
|
1425
|
+
self._addObject(ali)
|
1174
1426
|
|
1175
|
-
def addCommand(self, cmd
|
1427
|
+
def addCommand(self, cmd):
|
1176
1428
|
"""
|
1177
1429
|
向会话中增加一个命令。
|
1178
1430
|
|
1179
1431
|
:param cmd: 要增加的命令对象,应为 Command 类型或其子类
|
1180
1432
|
"""
|
1181
|
-
self._addObject(cmd
|
1433
|
+
self._addObject(cmd)
|
1182
1434
|
|
1183
1435
|
def addTrigger(self, tri: Trigger):
|
1184
1436
|
"""
|
@@ -1186,7 +1438,7 @@ class Session:
|
|
1186
1438
|
|
1187
1439
|
:param tri: 要增加的触发器对象,应为 Trigger 类型或其子类
|
1188
1440
|
"""
|
1189
|
-
self._addObject(tri
|
1441
|
+
self._addObject(tri)
|
1190
1442
|
|
1191
1443
|
def addTimer(self, ti: Timer):
|
1192
1444
|
"""
|
@@ -1194,7 +1446,7 @@ class Session:
|
|
1194
1446
|
|
1195
1447
|
:param ti: 要增加的定时器对象,应为 Timer 类型或其子类
|
1196
1448
|
"""
|
1197
|
-
self._addObject(ti
|
1449
|
+
self._addObject(ti)
|
1198
1450
|
|
1199
1451
|
def addGMCP(self, gmcp: GMCPTrigger):
|
1200
1452
|
"""
|
@@ -1203,7 +1455,7 @@ class Session:
|
|
1203
1455
|
:param gmcp: 要增加的GMCP触发器对象,应为 GMCPTrigger 类型或其子类
|
1204
1456
|
"""
|
1205
1457
|
|
1206
|
-
self._addObject(gmcp
|
1458
|
+
self._addObject(gmcp)
|
1207
1459
|
|
1208
1460
|
def delAlias(self, ali):
|
1209
1461
|
"""
|
@@ -1322,7 +1574,7 @@ class Session:
|
|
1322
1574
|
for ti in ti_s:
|
1323
1575
|
self.delTimer(ti)
|
1324
1576
|
|
1325
|
-
def delGMCP(self, gmcp
|
1577
|
+
def delGMCP(self, gmcp):
|
1326
1578
|
"""
|
1327
1579
|
从会话中移除一个GMCP触发器,可接受 GMCPTrigger 对象或其的id。使用方法与 delAlias 类似
|
1328
1580
|
|
@@ -1604,7 +1856,8 @@ class Session:
|
|
1604
1856
|
- #session
|
1605
1857
|
'''
|
1606
1858
|
|
1607
|
-
self.application.close_session()
|
1859
|
+
#self.application.close_session()
|
1860
|
+
self.application.act_close_session()
|
1608
1861
|
|
1609
1862
|
async def handle_wait(self, code: CodeLine = None, *args, **kwargs):
|
1610
1863
|
'''
|
@@ -1637,6 +1890,7 @@ class Session:
|
|
1637
1890
|
该函数不应该在代码中直接调用。
|
1638
1891
|
|
1639
1892
|
相关命令:
|
1893
|
+
- #disconnect
|
1640
1894
|
- #close
|
1641
1895
|
- #exit
|
1642
1896
|
'''
|
@@ -1658,6 +1912,18 @@ class Session:
|
|
1658
1912
|
|
1659
1913
|
self.info("已经与服务器连接了 {}".format(time_msg))
|
1660
1914
|
|
1915
|
+
def handle_disconnect(self, code: CodeLine = None, *args, **kwargs):
|
1916
|
+
'''
|
1917
|
+
嵌入命令 #disconnect / #dis 的执行函数,断开到远程服务器的连接(仅当远程服务器已连接时有效)。
|
1918
|
+
该函数不应该在代码中直接调用。
|
1919
|
+
|
1920
|
+
相关命令:
|
1921
|
+
- #connect
|
1922
|
+
- #close
|
1923
|
+
'''
|
1924
|
+
|
1925
|
+
self.disconnect()
|
1926
|
+
|
1661
1927
|
def handle_variable(self, code: CodeLine = None, *args, **kwargs):
|
1662
1928
|
'''
|
1663
1929
|
嵌入命令 #variable / #var 的执行函数,操作会话变量。
|
@@ -1677,7 +1943,10 @@ class Session:
|
|
1677
1943
|
- #global
|
1678
1944
|
'''
|
1679
1945
|
|
1680
|
-
|
1946
|
+
new_cmd_text, new_code = code.expand(self, *args, **kwargs)
|
1947
|
+
args = new_code[2:]
|
1948
|
+
|
1949
|
+
#args = code.code[2:]
|
1681
1950
|
|
1682
1951
|
if len(args) == 0:
|
1683
1952
|
vars = self._variables
|
@@ -1693,7 +1962,7 @@ class Session:
|
|
1693
1962
|
else:
|
1694
1963
|
vars_simple[k] = v
|
1695
1964
|
|
1696
|
-
width = self.application.get_width()
|
1965
|
+
width = self.application.get_width() - 2 # 保留2个字符,防止 > 导致换行
|
1697
1966
|
|
1698
1967
|
title = f" VARIABLE LIST IN SESSION {self.name} "
|
1699
1968
|
left = (width - len(title)) // 2
|
@@ -1701,6 +1970,7 @@ class Session:
|
|
1701
1970
|
self.writetobuffer("="*left + title + "="*right, newline = True)
|
1702
1971
|
|
1703
1972
|
# print vars in simple, 每个变量占40格,一行可以多个变量
|
1973
|
+
# 这里可以考虑调整一下,默认40, 但如果一个变量值太长,则选择占两个位置
|
1704
1974
|
var_count = len(vars_simple)
|
1705
1975
|
var_per_line = (width - 2) // 40
|
1706
1976
|
lines = math.ceil(var_count / var_per_line)
|
@@ -1716,14 +1986,18 @@ class Session:
|
|
1716
1986
|
self.writetobuffer(" " * left_space)
|
1717
1987
|
line_vars = var_keys[start:end]
|
1718
1988
|
for var in line_vars:
|
1719
|
-
|
1989
|
+
repr = vars_simple[var].__repr__()
|
1990
|
+
vwidth = 22 - (wcswidth(repr) - len(repr))
|
1991
|
+
self.writetobuffer("{0} = {1}".format(var.rjust(20), repr.ljust(vwidth)))
|
1992
|
+
#self.writetobuffer("{0:>18} = {1:<19}".format(var, vars_simple[var].__repr__()))
|
1720
1993
|
|
1721
1994
|
self.writetobuffer("", newline = True)
|
1722
1995
|
|
1723
1996
|
# print vars in complex, 每个变量占1行
|
1724
|
-
|
1997
|
+
var_keys = sorted(vars_complex.keys())
|
1998
|
+
for key in var_keys:
|
1725
1999
|
self.writetobuffer(" " * left_space)
|
1726
|
-
self.writetobuffer("{0:>
|
2000
|
+
self.writetobuffer("{0:>20} = {1}".format(key, vars_complex[key].__repr__()), newline = True)
|
1727
2001
|
|
1728
2002
|
self.writetobuffer("="*width, newline = True)
|
1729
2003
|
|
@@ -1735,7 +2009,13 @@ class Session:
|
|
1735
2009
|
self.warning(f"当前session中不存在名称为 {args[0]} 的变量")
|
1736
2010
|
|
1737
2011
|
elif len(args) == 2:
|
1738
|
-
|
2012
|
+
val = None
|
2013
|
+
try:
|
2014
|
+
val = eval(args[1])
|
2015
|
+
except:
|
2016
|
+
val = args[1]
|
2017
|
+
|
2018
|
+
self.setVariable(args[0], val)
|
1739
2019
|
|
1740
2020
|
def handle_global(self, code: CodeLine = None, *args, **kwargs):
|
1741
2021
|
'''
|
@@ -1756,7 +2036,9 @@ class Session:
|
|
1756
2036
|
- #variable
|
1757
2037
|
'''
|
1758
2038
|
|
1759
|
-
|
2039
|
+
new_cmd_text, new_code = code.expand(self, *args, **kwargs)
|
2040
|
+
args = new_code[2:]
|
2041
|
+
#args = code.code[2:]
|
1760
2042
|
|
1761
2043
|
if len(args) == 0:
|
1762
2044
|
vars = self.application.globals
|
@@ -1768,7 +2050,7 @@ class Session:
|
|
1768
2050
|
else:
|
1769
2051
|
vars_simple[k] = v
|
1770
2052
|
|
1771
|
-
width = self.application.get_width()
|
2053
|
+
width = self.application.get_width() - 2 # 保留2个字符,防止 > 导致换行
|
1772
2054
|
|
1773
2055
|
title = f" GLOBAL VARIABLES LIST "
|
1774
2056
|
left = (width - len(title)) // 2
|
@@ -1791,26 +2073,33 @@ class Session:
|
|
1791
2073
|
self.writetobuffer(" " * left_space)
|
1792
2074
|
line_vars = var_keys[start:end]
|
1793
2075
|
for var in line_vars:
|
1794
|
-
|
2076
|
+
repr = vars_simple[var].__repr__()
|
2077
|
+
vwidth = 22 - (wcswidth(repr) - len(repr))
|
2078
|
+
self.writetobuffer("{0} = {1}".format(var.rjust(20), repr.ljust(vwidth)))
|
1795
2079
|
|
1796
2080
|
self.writetobuffer("", newline = True)
|
1797
2081
|
|
1798
2082
|
# print vars in complex, 每个变量占1行
|
1799
2083
|
for k, v in vars_complex.items():
|
1800
2084
|
self.writetobuffer(" " * left_space)
|
1801
|
-
self.writetobuffer("{0:>
|
2085
|
+
self.writetobuffer("{0:>20} = {1}".format(k, v.__repr__()), newline = True)
|
1802
2086
|
|
1803
2087
|
self.writetobuffer("="*width, newline = True)
|
1804
2088
|
|
1805
2089
|
elif len(args) == 1:
|
1806
2090
|
var = args[0]
|
1807
2091
|
if var in self.application.globals.keys():
|
1808
|
-
self.info("{0:>
|
2092
|
+
self.info("{0:>20} = {1:<22}".format(var, self.application.get_globals(var).__repr__()), "全局变量")
|
1809
2093
|
else:
|
1810
2094
|
self.info("全局空间不存在名称为 {} 的变量".format(var), "全局变量")
|
1811
2095
|
|
1812
2096
|
elif len(args) == 2:
|
1813
|
-
|
2097
|
+
val = None
|
2098
|
+
try:
|
2099
|
+
val = eval(args[1])
|
2100
|
+
except:
|
2101
|
+
val = args[1]
|
2102
|
+
self.application.set_globals(args[0], val)
|
1814
2103
|
|
1815
2104
|
def _handle_objs(self, name: str, objs: dict, *args):
|
1816
2105
|
if len(args) == 0:
|
@@ -1875,7 +2164,7 @@ class Session:
|
|
1875
2164
|
self.info("创建Timer {} 成功: {}".format(ti.id, ti.__repr__()))
|
1876
2165
|
|
1877
2166
|
def handle_alias(self, code: CodeLine = None, *args, **kwargs):
|
1878
|
-
|
2167
|
+
r"""
|
1879
2168
|
嵌入命令 #alias / #ali 的执行函数,操作别名。该命令可以不带参数、带一个参数或者两个参数。
|
1880
2169
|
该函数不应该在代码中直接调用。
|
1881
2170
|
|
@@ -1906,7 +2195,7 @@ class Session:
|
|
1906
2195
|
- #trigger
|
1907
2196
|
- #timer
|
1908
2197
|
- #command
|
1909
|
-
|
2198
|
+
"""
|
1910
2199
|
|
1911
2200
|
self._handle_objs("Alias", self._aliases, *code.code[2:])
|
1912
2201
|
|
@@ -2265,6 +2554,7 @@ class Session:
|
|
2265
2554
|
self._variables.clear()
|
2266
2555
|
self._tasks.clear()
|
2267
2556
|
|
2557
|
+
|
2268
2558
|
def load_module(self, module_names):
|
2269
2559
|
"""
|
2270
2560
|
模块加载函数。
|
@@ -2288,27 +2578,12 @@ class Session:
|
|
2288
2578
|
"加载指定名称模块"
|
2289
2579
|
try:
|
2290
2580
|
if module_name not in self._modules.keys():
|
2291
|
-
|
2292
|
-
if hasattr(mod, 'Configuration'):
|
2293
|
-
config = mod.Configuration(self)
|
2294
|
-
self._modules[module_name] = {"module": mod, "config": config}
|
2295
|
-
self.info(f"主配置模块 {module_name} 加载完成.")
|
2296
|
-
else:
|
2297
|
-
self._modules[module_name] = {"module": mod, "config": None}
|
2298
|
-
self.info(f"子配置模块 {module_name} 加载完成.")
|
2581
|
+
self._modules[module_name] = ModuleInfo(module_name, self)
|
2299
2582
|
|
2300
2583
|
else:
|
2301
|
-
mod = self._modules[module_name]
|
2302
|
-
|
2303
|
-
|
2304
|
-
mod = importlib.reload(mod)
|
2305
|
-
if hasattr(mod, 'Configuration'):
|
2306
|
-
config = mod.Configuration(self)
|
2307
|
-
self._modules[module_name] = {"module": mod, "config": config}
|
2308
|
-
self.info(f"主配置模块 {module_name} 重新加载完成.")
|
2309
|
-
else:
|
2310
|
-
self._modules[module_name] = {"module": mod, "config": None}
|
2311
|
-
self.info(f"子配置模块 {module_name} 重新加载完成.")
|
2584
|
+
mod = self._modules[module_name]
|
2585
|
+
if isinstance(mod, ModuleInfo):
|
2586
|
+
mod.reload()
|
2312
2587
|
|
2313
2588
|
except Exception as e:
|
2314
2589
|
import traceback
|
@@ -2334,20 +2609,11 @@ class Session:
|
|
2334
2609
|
self._unload_module(mod)
|
2335
2610
|
|
2336
2611
|
def _unload_module(self, module_name):
|
2337
|
-
"卸载指定名称模块。卸载支持需要模块的Configuration实现
|
2612
|
+
"卸载指定名称模块。卸载支持需要模块的Configuration实现 __unload__ 或 unload 方法"
|
2338
2613
|
if module_name in self._modules.keys():
|
2339
|
-
mod = self._modules
|
2340
|
-
|
2341
|
-
|
2342
|
-
if hasattr(config, "unload"):
|
2343
|
-
unload = getattr(config, "unload", None)
|
2344
|
-
if callable(unload):
|
2345
|
-
unload(config)
|
2346
|
-
|
2347
|
-
del config
|
2348
|
-
del mod
|
2349
|
-
self._modules.pop(module_name)
|
2350
|
-
self.info(f"配置模块 {module_name} 已成功卸载.")
|
2614
|
+
mod = self._modules.pop(module_name)
|
2615
|
+
if isinstance(mod, ModuleInfo):
|
2616
|
+
mod.unload()
|
2351
2617
|
|
2352
2618
|
else:
|
2353
2619
|
self.warning(f"指定模块名称 {module_name} 并未加载.")
|
@@ -2361,9 +2627,9 @@ class Session:
|
|
2361
2627
|
:param module_names: 要重新加载的模块清单。为元组/列表时,卸载指定名称的系列模块,当名称为字符串时,卸载单个模块。当不指定时,重新加载所有已加载模块。
|
2362
2628
|
"""
|
2363
2629
|
if module_names is None:
|
2364
|
-
self.
|
2365
|
-
|
2366
|
-
|
2630
|
+
for name, module in self._modules.items():
|
2631
|
+
if isinstance(module, ModuleInfo):
|
2632
|
+
module.reload()
|
2367
2633
|
|
2368
2634
|
self.info(f"所有配置模块全部重新加载完成.")
|
2369
2635
|
|
@@ -2371,14 +2637,17 @@ class Session:
|
|
2371
2637
|
for mod in module_names:
|
2372
2638
|
mod = mod.strip()
|
2373
2639
|
if mod in self._modules.keys():
|
2374
|
-
self.
|
2640
|
+
module = self._modules[mod]
|
2641
|
+
if isinstance(module, ModuleInfo):
|
2642
|
+
module.reload()
|
2375
2643
|
else:
|
2376
2644
|
self.warning(f"指定模块名称 {mod} 并未加载,无法重新加载.")
|
2377
2645
|
|
2378
2646
|
elif isinstance(module_names, str):
|
2379
2647
|
if module_names in self._modules.keys():
|
2380
|
-
|
2381
|
-
|
2648
|
+
module = self._modules[module_names]
|
2649
|
+
if isinstance(module, ModuleInfo):
|
2650
|
+
module.reload()
|
2382
2651
|
else:
|
2383
2652
|
self.warning(f"指定模块名称 {module_names} 并未加载,无法重新加载.")
|
2384
2653
|
|
@@ -2462,7 +2731,7 @@ class Session:
|
|
2462
2731
|
|
2463
2732
|
elif mod in self.plugins.keys():
|
2464
2733
|
self.application.reload_plugin(self.plugins[mod])
|
2465
|
-
|
2734
|
+
self.info(f'插件 {mod} 重新加载完成!')
|
2466
2735
|
else:
|
2467
2736
|
self.warning(f"指定名称 {mod} 既未找到模块,也未找到插件,重新加载失败..")
|
2468
2737
|
|
@@ -2516,12 +2785,29 @@ class Session:
|
|
2516
2785
|
- #reload
|
2517
2786
|
'''
|
2518
2787
|
|
2519
|
-
|
2520
|
-
|
2521
|
-
|
2522
|
-
|
2523
|
-
|
2788
|
+
args = code.code[2:]
|
2789
|
+
|
2790
|
+
if len(args) == 0:
|
2791
|
+
count = len(self._modules.keys())
|
2792
|
+
if count == 0:
|
2793
|
+
self.info("当前会话并未加载任何模块。", "MODULES")
|
2794
|
+
else:
|
2795
|
+
self.info(f"当前会话已加载 {count} 个模块,包括(按加载顺序排列):{list(self._modules.keys())}", "MODULES")
|
2524
2796
|
|
2797
|
+
elif len(args) >= 1:
|
2798
|
+
modules = ",".join(args).split(",")
|
2799
|
+
for mod in modules:
|
2800
|
+
if mod in self._modules.keys():
|
2801
|
+
module = self._modules[mod]
|
2802
|
+
if isinstance(module, ModuleInfo):
|
2803
|
+
if module.ismainmodule:
|
2804
|
+
self.info(f"模块 {module.name} 中包含的配置包括: {', '.join(module.config.keys())}")
|
2805
|
+
else:
|
2806
|
+
self.info(f"模块 {module.name} 为子模块,不包含配置。")
|
2807
|
+
|
2808
|
+
else:
|
2809
|
+
self.info(f"本会话中不存在指定名称 {mod} 的模块,可能是尚未加载到本会话中")
|
2810
|
+
|
2525
2811
|
def handle_reset(self, code: CodeLine = None, *args, **kwargs):
|
2526
2812
|
'''
|
2527
2813
|
嵌入命令 #reset 的执行函数,复位全部脚本。该命令不带参数。
|
@@ -2541,7 +2827,8 @@ class Session:
|
|
2541
2827
|
|
2542
2828
|
def handle_save(self, code: CodeLine = None, *args, **kwargs):
|
2543
2829
|
'''
|
2544
|
-
嵌入命令 #save
|
2830
|
+
嵌入命令 #save 的执行函数,保存当前会话变量(系统变量和临时变量除外)至文件。该命令不带参数。
|
2831
|
+
系统变量包括 %line, %copy 和 %raw 三个,临时变量是指变量名已下划线开头的变量
|
2545
2832
|
该函数不应该在代码中直接调用。
|
2546
2833
|
|
2547
2834
|
使用:
|
@@ -2562,10 +2849,10 @@ class Session:
|
|
2562
2849
|
with open(file, "wb") as fp:
|
2563
2850
|
saved = dict()
|
2564
2851
|
saved.update(self._variables)
|
2565
|
-
|
2566
|
-
|
2567
|
-
|
2568
|
-
|
2852
|
+
keys = list(saved.keys())
|
2853
|
+
for key in keys:
|
2854
|
+
if key.startswith("_"):
|
2855
|
+
saved.pop(key)
|
2569
2856
|
saved.pop("%line", None)
|
2570
2857
|
saved.pop("%raw", None)
|
2571
2858
|
saved.pop("%copy", None)
|
@@ -2664,7 +2951,7 @@ class Session:
|
|
2664
2951
|
if name in self.plugins.keys():
|
2665
2952
|
plugin = self.plugins[name]
|
2666
2953
|
self.info(f"{plugin.desc['DESCRIPTION']}, 版本 {plugin.desc['VERSION']} 作者 {plugin.desc['AUTHOR']} 发布日期 {plugin.desc['RELEASE_DATE']}", f"PLUGIN {name}")
|
2667
|
-
self.writetobuffer(plugin.help)
|
2954
|
+
self.writetobuffer(plugin.help, True)
|
2668
2955
|
|
2669
2956
|
def handle_replace(self, code: CodeLine = None, *args, **kwargs):
|
2670
2957
|
'''
|
@@ -2775,6 +3062,8 @@ class Session:
|
|
2775
3062
|
self.error(new_text[6:])
|
2776
3063
|
|
2777
3064
|
def info2(self, msg, title = "PYMUD INFO", style = Settings.INFO_STYLE):
|
3065
|
+
msg = f"{msg}"
|
3066
|
+
|
2778
3067
|
if Settings.client["newline"] in msg:
|
2779
3068
|
new_lines = list()
|
2780
3069
|
msg_lines = msg.split(Settings.client["newline"])
|
@@ -2814,3 +3103,105 @@ class Session:
|
|
2814
3103
|
:param style: 要输出信息的格式(ANSI), 默认为 ERR_STYLE, \x1b[31m
|
2815
3104
|
"""
|
2816
3105
|
self.info2(msg, title, style)
|
3106
|
+
|
3107
|
+
def handle_log(self, code: CodeLine = None, *args, **kwargs):
|
3108
|
+
'''
|
3109
|
+
嵌入命令 #log 的执行函数,控制当前会话的记录状态。
|
3110
|
+
该函数不应该在代码中直接调用。
|
3111
|
+
|
3112
|
+
使用:
|
3113
|
+
- #log : 显示所有记录器的状态情况
|
3114
|
+
- #log start [logger-name] [-a|-w|-n] [-r] : 启动一个记录器
|
3115
|
+
|
3116
|
+
参数:
|
3117
|
+
- :logger-name: 记录器名称。当不指定时,选择名称为会话名称的记录器(会话默认记录器)
|
3118
|
+
- :-a|-w|-n: 记录器模式选择。 -a 为添加模式(未指定时默认值),在原记录文件后端添加; -w 为覆写模式,清空原记录文件并重新记录; -n 为新建模式,以名称和当前时间为参数,使用 name.now.log 形式创建新的记录文件
|
3119
|
+
- :-r: 指定记录器是否使用 raw 模式
|
3120
|
+
|
3121
|
+
- #log stop [logger-name] : 停止一个记录器
|
3122
|
+
|
3123
|
+
参数:
|
3124
|
+
- :logger-name: 记录器名称。当不指定时,选择名称为会话名称的记录器(会话默认记录器)
|
3125
|
+
|
3126
|
+
- #log show [loggerFile]: 显示全部日志记录或指定记录文件
|
3127
|
+
|
3128
|
+
参数:
|
3129
|
+
- :loggerFile: 要显示的记录文件名称。当不指定时,弹出对话框列出当前目录下所有记录文件
|
3130
|
+
|
3131
|
+
示例:
|
3132
|
+
- ``#log`` : 在当前会话窗口列出所有记录器状态
|
3133
|
+
- ``#log start`` : 启动本会话默认记录器(记录器名为会话名)。该记录器以纯文本模式,将后续所有屏幕输出、键盘键入、命令输入等记录到 log 目录下 name.log 文件的后端
|
3134
|
+
- ``#log start -r`` : 启动本会话默认记录器。该记录器以raw模式,将后续所有屏幕输出、键盘键入、命令输入等记录到 log 目录下 name.log 文件的后端
|
3135
|
+
- ``#log start chat`` : 启动名为 chat 的记录器。该记录器以纯文本模式,记录代码中调用过该记录器 .log 进行记录的信息
|
3136
|
+
- ``#log stop`` : 停止本会话默认记录器(记录器名为会话名)。
|
3137
|
+
|
3138
|
+
注意:
|
3139
|
+
- 记录器文件模式(-a|-w|-n)在修改后,只有在下一次该记录器启动时才会生效
|
3140
|
+
- 记录器记录模式(-r)在修改后立即生效
|
3141
|
+
'''
|
3142
|
+
|
3143
|
+
args = list()
|
3144
|
+
if isinstance(code, CodeLine):
|
3145
|
+
args = code.code[2:]
|
3146
|
+
|
3147
|
+
if len(args) == 0:
|
3148
|
+
session_loggers = set(self._loggers.keys())
|
3149
|
+
app_loggers = set(self.application.loggers.keys()).difference(session_loggers)
|
3150
|
+
self.info("本会话中的记录器情况:")
|
3151
|
+
for name in session_loggers:
|
3152
|
+
logger = self.application.loggers[name]
|
3153
|
+
self.info(f"记录器 {logger.name}, 当前状态: {'开启' if logger.enabled else '关闭'}, 文件模式: {logger.mode}, 记录模式: {'ANSI' if logger.raw else '纯文本'}")
|
3154
|
+
|
3155
|
+
if len(app_loggers) > 0:
|
3156
|
+
self.info("本应用其他会话中的记录器情况:")
|
3157
|
+
for name in app_loggers:
|
3158
|
+
logger = self.application.loggers[name]
|
3159
|
+
self.info(f"记录器 {logger.name}, 当前状态: {'开启' if logger.enabled else '关闭'}, 文件模式: {logger.mode}, 记录模式: {'ANSI' if logger.raw else '纯文本'}")
|
3160
|
+
|
3161
|
+
else:
|
3162
|
+
name = self.name
|
3163
|
+
if len(args) > 1 and not args[1].startswith('-'):
|
3164
|
+
name = args[1]
|
3165
|
+
|
3166
|
+
if (args[0] == "start"):
|
3167
|
+
if "-n" in args:
|
3168
|
+
mode = "n"
|
3169
|
+
mode_name = '新建'
|
3170
|
+
elif "-w" in args:
|
3171
|
+
mode = "w"
|
3172
|
+
mode_name = '覆写'
|
3173
|
+
else:
|
3174
|
+
mode = "a"
|
3175
|
+
mode_name = '添加'
|
3176
|
+
|
3177
|
+
raw = True if "-r" in args else False
|
3178
|
+
raw_name = '原始ANSI' if raw else '纯文本'
|
3179
|
+
|
3180
|
+
logger = self.getLogger(name = name, mode = mode, raw = raw)
|
3181
|
+
logger.enabled = True
|
3182
|
+
|
3183
|
+
self.info(f"{datetime.datetime.now()}: 记录器{name}以{mode_name}文件模式以及{raw_name}记录模式开启。")
|
3184
|
+
|
3185
|
+
elif (args[0] == "stop"):
|
3186
|
+
self.info(f"{datetime.datetime.now()}: 记录器{name}记录已关闭。")
|
3187
|
+
self.log.enabled = False
|
3188
|
+
|
3189
|
+
elif (args[0] == "show"):
|
3190
|
+
if len(args) > 1 and not args[1].startswith('-'):
|
3191
|
+
file = args[1]
|
3192
|
+
if os.path.exists(file):
|
3193
|
+
filepath = os.path.abspath(file)
|
3194
|
+
#self.info(f'file {filepath} exists, will be shown.')
|
3195
|
+
self.application.logFileShown = filepath
|
3196
|
+
self.application.showLogInTab()
|
3197
|
+
elif os.path.exists(os.path.join('./log', file)):
|
3198
|
+
filepath = os.path.abspath(os.path.join('./log', file))
|
3199
|
+
#self.info(f'file {filepath} exists, will be shown.')
|
3200
|
+
self.application.logFileShown = filepath
|
3201
|
+
self.application.showLogInTab()
|
3202
|
+
else:
|
3203
|
+
self.warning(f'指定记录文件 {file} 不存在!')
|
3204
|
+
|
3205
|
+
else:
|
3206
|
+
self.application.show_logSelectDialog()
|
3207
|
+
|