pymud 0.19.4__py3-none-any.whl → 0.20.0a1__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 +3 -1
- pymud/__main__.py +86 -48
- pymud/dialogs.py +28 -1
- pymud/extras.py +4 -2
- pymud/logger.py +154 -0
- pymud/objects.py +1 -1
- pymud/pymud.py +136 -18
- pymud/session.py +150 -12
- pymud/settings.py +4 -3
- {pymud-0.19.4.dist-info → pymud-0.20.0a1.dist-info}/METADATA +7 -1
- pymud-0.20.0a1.dist-info/RECORD +17 -0
- {pymud-0.19.4.dist-info → pymud-0.20.0a1.dist-info}/WHEEL +1 -1
- pymud-0.19.4.dist-info/RECORD +0 -16
- {pymud-0.19.4.dist-info → pymud-0.20.0a1.dist-info}/LICENSE.txt +0 -0
- {pymud-0.19.4.dist-info → pymud-0.20.0a1.dist-info}/entry_points.txt +0 -0
- {pymud-0.19.4.dist-info → pymud-0.20.0a1.dist-info}/top_level.txt +0 -0
pymud/__init__.py
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
from .settings import Settings
|
2
|
+
from .pymud import PyMudApp
|
2
3
|
from .objects import CodeBlock, Alias, SimpleAlias, Trigger, SimpleTrigger, Command, SimpleCommand, Timer, SimpleTimer, GMCPTrigger
|
3
4
|
from .extras import DotDict
|
4
5
|
from .session import Session
|
6
|
+
from .logger import Logger
|
5
7
|
|
6
8
|
__all__ = [
|
7
|
-
"Settings", "CodeBlock", "Alias", "SimpleAlias", "Trigger", "SimpleTrigger", "Command", "SimpleCommand", "Timer", "SimpleTimer", "GMCPTrigger", "Session", "PyMudApp", "DotDict"
|
9
|
+
"PyMudApp", "Settings", "CodeBlock", "Alias", "SimpleAlias", "Trigger", "SimpleTrigger", "Command", "SimpleCommand", "Timer", "SimpleTimer", "GMCPTrigger", "Session", "PyMudApp", "DotDict", "Logger"
|
8
10
|
]
|
pymud/__main__.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import sys, os, json, platform, shutil, logging
|
1
|
+
import sys, os, json, platform, shutil, logging, argparse
|
2
2
|
from .pymud import main
|
3
3
|
from .settings import Settings
|
4
4
|
|
@@ -40,63 +40,101 @@ CFG_TEMPLATE = {
|
|
40
40
|
}
|
41
41
|
}
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
43
|
+
def init_pymud_env(args):
|
44
|
+
print(f"欢迎使用PyMUD, 版本{Settings.__version__}. 使用PyMUD时, 建议建立一个新目录(任意位置),并将自己的脚本以及配置文件放到该目录下.")
|
45
|
+
print("即将开始为首次运行初始化环境...")
|
46
|
+
system = platform.system().lower()
|
47
|
+
|
48
|
+
dir = args.dir
|
49
|
+
if dir:
|
50
|
+
print(f"你已经指定了创建脚本的目录为 {args.dir}, 将不再检测操作系统")
|
51
|
+
|
52
|
+
else:
|
50
53
|
if system == "windows":
|
51
|
-
dir = input("检测到当前系统为Windows, 请指定游戏脚本的目录(若目录不存在会自动创建),直接回车表示使用默认值[d:\pkuxkx
|
52
|
-
if not dir: dir = "d:\\pkuxkx
|
54
|
+
dir = input("检测到当前系统为Windows, 请指定游戏脚本的目录(若目录不存在会自动创建),直接回车表示使用默认值[d:\pkuxkx]:")
|
55
|
+
if not dir: dir = "d:\\pkuxkx"
|
56
|
+
|
53
57
|
elif system == "linux":
|
54
|
-
dir = input("检测到当前系统为Linux, 请指定游戏脚本的目录(若目录不存在会自动创建),直接回车表示使用默认值[~/pkuxkx
|
55
|
-
if not dir: dir = "~/pkuxkx
|
58
|
+
dir = input("检测到当前系统为Linux, 请指定游戏脚本的目录(若目录不存在会自动创建),直接回车表示使用默认值[~/pkuxkx]:")
|
59
|
+
if not dir: dir = "~/pkuxkx"
|
56
60
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
os.chdir(dir)
|
61
|
-
print(f'完成!')
|
61
|
+
elif system == "darwin":
|
62
|
+
dir = input("检测到当前系统为MacOS, 请指定游戏脚本的目录(若目录不存在会自动创建),直接回车表示使用默认值[~/pkuxkx]:")
|
63
|
+
if not dir: dir = "~/pkuxkx"
|
62
64
|
|
63
|
-
if os.path.exists('pymud.cfg'):
|
64
|
-
print(f'检测到脚本目录下已存在pymud.cfg文件,将直接使用此文件进入PyMUD...')
|
65
|
-
else:
|
66
|
-
print(f'检测到脚本目录下不存在pymud.cfg文件,将使用默认内容创建该配置文件...')
|
67
|
-
with open('pymud.cfg', mode = 'x') as fp:
|
68
|
-
fp.writelines(json.dumps(CFG_TEMPLATE, indent = 4))
|
69
|
-
|
70
|
-
if not os.path.exists('examples.py'):
|
71
|
-
from pymud import pkuxkx
|
72
|
-
module_dir = pkuxkx.__file__
|
73
|
-
shutil.copyfile(module_dir, 'examples.py')
|
74
|
-
print(f'已将样例脚本拷贝至脚本目录,并加入默认配置文件')
|
75
|
-
|
76
|
-
print(f"后续可自行修改 {dir} 目录下的 pymud.cfg 文件以进行配置。")
|
77
|
-
if system == "linux":
|
78
|
-
print(f"后续运行PyMUD, 请在 {dir} 目录下键入命令: python3 -m pymud")
|
79
65
|
else:
|
80
|
-
print(f"
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
66
|
+
print(f"当前系统不是Windows、Linux或MacOS, 无法通过init来进行配置, 请手动配置. 默认配置即将推出")
|
67
|
+
|
68
|
+
|
69
|
+
if not os.path.exists(dir):
|
70
|
+
print(f'检测到给定目录 {dir} 不存在,正在创建目录...', end = "")
|
71
|
+
os.mkdir(dir)
|
72
|
+
os.chdir(dir)
|
73
|
+
print(f'完成!')
|
74
|
+
|
75
|
+
if os.path.exists('pymud.cfg'):
|
76
|
+
print(f'检测到脚本目录下已存在pymud.cfg文件,将直接使用此文件进入PyMUD...')
|
77
|
+
else:
|
78
|
+
print(f'检测到脚本目录下不存在pymud.cfg文件,将使用默认内容创建该配置文件...')
|
79
|
+
with open('pymud.cfg', mode = 'x') as fp:
|
80
|
+
fp.writelines(json.dumps(CFG_TEMPLATE, indent = 4))
|
81
|
+
|
82
|
+
if not os.path.exists('examples.py'):
|
83
|
+
from pymud import pkuxkx
|
84
|
+
module_dir = pkuxkx.__file__
|
85
|
+
shutil.copyfile(module_dir, 'examples.py')
|
86
|
+
print(f'已将样例脚本拷贝至脚本目录,并加入默认配置文件')
|
87
|
+
|
88
|
+
print(f"后续可自行修改 {dir} 目录下的 pymud.cfg 文件以进行配置。")
|
89
|
+
if system == "windows":
|
90
|
+
print(f"后续运行PyMUD, 请在 {dir} 目录下键入命令: python -m pymud")
|
93
91
|
else:
|
94
|
-
|
92
|
+
print(f"后续运行PyMUD, 请在 {dir} 目录下键入命令: python3 -m pymud")
|
95
93
|
|
94
|
+
input('所有内容已初始化完毕, 请按回车进入PyMUD.')
|
95
|
+
|
96
|
+
module_entry(args)
|
97
|
+
|
98
|
+
def module_entry(args):
|
99
|
+
if args.debug:
|
100
|
+
logging.basicConfig(level = logging.NOTSET,
|
101
|
+
format = '%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
|
102
|
+
datefmt = '%m-%d %H:%M',
|
103
|
+
filename = args.logfile,
|
104
|
+
filemode = 'a' if args.filemode else 'w',
|
105
|
+
encoding = "utf-8"
|
106
|
+
)
|
107
|
+
|
108
|
+
else:
|
109
|
+
logging.basicConfig(level = logging.NOTSET,
|
110
|
+
format = '%(asctime)s %(name)-12s: %(message)s',
|
111
|
+
datefmt = '%m-%d %H:%M',
|
112
|
+
handlers = [logging.NullHandler()],
|
113
|
+
)
|
114
|
+
|
96
115
|
cfg = "pymud.cfg"
|
97
116
|
if os.path.exists(cfg):
|
98
117
|
with open(cfg, "r", encoding="utf8", errors="ignore") as fp:
|
99
118
|
cfg_data = json.load(fp)
|
100
119
|
main(cfg_data)
|
101
120
|
else:
|
102
|
-
main()
|
121
|
+
main()
|
122
|
+
|
123
|
+
|
124
|
+
if __name__ == "__main__":
|
125
|
+
parser = argparse.ArgumentParser(prog = "pymud", usage = "python -m pymud [-h] [-d] [-l logfile] [-a] {init} ...", description = "PyMUD命令行参数帮助")
|
126
|
+
subparsers = parser.add_subparsers(help = 'init用于初始化运行环境')
|
127
|
+
|
128
|
+
par_init = subparsers.add_parser('init', usage = "python -m pymud init [-h] [-d dir]", description = '初始化pymud运行环境, 包括建立脚本目录, 创建默认配置文件, 创建样例脚本等.')
|
129
|
+
par_init.add_argument('-d', '--dir', dest = 'dir', metavar = 'dir', type = str, default = '', help = '指定构建脚本目录的名称, 不指定时会根据操作系统选择不同默认值')
|
130
|
+
par_init.set_defaults(func = init_pymud_env)
|
131
|
+
|
132
|
+
parser.add_argument('-d', '--debug', dest = 'debug', action = 'store_true', default = False, help = '指定以调试模式进入PyMUD。此时,系统log等级将设置为logging.NOTSET, 所有log数据均会被记录。默认不启用。')
|
133
|
+
parser.add_argument('-l', '--logfile', dest = 'logfile', metavar = 'logfile', default = 'pymud.log', help = '指定调试模式下记录文件名,不指定时,默认为当前目录下的pymud.log')
|
134
|
+
parser.add_argument('-a', '--appendmode', dest = 'filemode', action = 'store_true', default = True, help = '指定log文件的访问模式是否为append尾部添加模式,默认为True。当为False时,使用w模式,即每次运行清空之前记录')
|
135
|
+
|
136
|
+
args=parser.parse_args()
|
137
|
+
if hasattr(args, 'func'):
|
138
|
+
args.func(args)
|
139
|
+
else:
|
140
|
+
module_entry(args)
|
pymud/dialogs.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import asyncio, webbrowser
|
2
2
|
|
3
3
|
from prompt_toolkit.layout import AnyContainer, ConditionalContainer, Float, VSplit, HSplit, Window, WindowAlign, ScrollablePane, ScrollOffsets
|
4
|
-
from prompt_toolkit.widgets import Button, Dialog, Label, MenuContainer, MenuItem, TextArea, SystemToolbar, Frame
|
4
|
+
from prompt_toolkit.widgets import Button, Dialog, Label, MenuContainer, MenuItem, TextArea, SystemToolbar, Frame, RadioList
|
5
5
|
from prompt_toolkit.layout.dimension import Dimension, D
|
6
6
|
from prompt_toolkit import ANSI, HTML
|
7
7
|
from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
|
@@ -127,3 +127,30 @@ class NewSessionDialog(BasicDialog):
|
|
127
127
|
encoding = get_app().layout.get_buffer_by_name("encoding").text
|
128
128
|
result = (name, host, port, encoding)
|
129
129
|
self.set_done(result)
|
130
|
+
|
131
|
+
|
132
|
+
class LogSelectionDialog(BasicDialog):
|
133
|
+
def __init__(self, text, values, modal=True):
|
134
|
+
self._header_text = text
|
135
|
+
self._selection_values = values
|
136
|
+
self._radio_list = RadioList(values = self._selection_values)
|
137
|
+
super().__init__('选择查看的记录', modal)
|
138
|
+
|
139
|
+
def create_body(self) -> AnyContainer:
|
140
|
+
|
141
|
+
|
142
|
+
body=HSplit([
|
143
|
+
Label(text = self._header_text, dont_extend_height=True),
|
144
|
+
self._radio_list
|
145
|
+
])
|
146
|
+
return body
|
147
|
+
|
148
|
+
def create_buttons(self):
|
149
|
+
ok_button = EasternButton(text="确定", handler=self.btn_ok_clicked)
|
150
|
+
cancel_button = EasternButton(text="取消", handler=(lambda: self.set_done(False)))
|
151
|
+
return [ok_button, cancel_button]
|
152
|
+
|
153
|
+
def btn_ok_clicked(self):
|
154
|
+
result = self._radio_list.current_value
|
155
|
+
self.set_done(result)
|
156
|
+
|
pymud/extras.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
from unicodedata import east_asian_width
|
3
3
|
from wcwidth import wcwidth
|
4
4
|
from typing import Any
|
5
|
-
import time
|
5
|
+
import time, datetime
|
6
6
|
|
7
7
|
from typing import Iterable
|
8
8
|
from prompt_toolkit import ANSI
|
@@ -434,6 +434,7 @@ class SessionBufferControl(BufferControl):
|
|
434
434
|
buffer.cursor_position = start
|
435
435
|
buffer.start_selection(selection_type=SelectionType.CHARACTERS)
|
436
436
|
buffer.cursor_position = end
|
437
|
+
|
437
438
|
else:
|
438
439
|
# Don't handle scroll events here.
|
439
440
|
return NotImplemented
|
@@ -1078,4 +1079,5 @@ class Plugin:
|
|
1078
1079
|
|
1079
1080
|
def __getattr__(self, __name: str) -> Any:
|
1080
1081
|
if hasattr(self.mod, __name):
|
1081
|
-
return self.mod.__getattribute__(__name)
|
1082
|
+
return self.mod.__getattribute__(__name)
|
1083
|
+
|
pymud/logger.py
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
import os, re, datetime, threading
|
2
|
+
from queue import SimpleQueue, Empty
|
3
|
+
|
4
|
+
class Logger:
|
5
|
+
"""
|
6
|
+
PyMUD 的记录器类型,可用于会话中向文件记录数据
|
7
|
+
|
8
|
+
:param name: 记录器名称,各记录器名称应保持唯一。记录器名称会作为记录文件名称的主要参数
|
9
|
+
:param mode: 记录模式。可选模式包括 a, w, n 三种。
|
10
|
+
- a为添加模式,当新开始记录时,会添加在原有记录文件(name.log)之后。
|
11
|
+
- w为覆写模式,当新开始记录时,会覆写原记录文件(name.log)。
|
12
|
+
- n为新建模式,当新开始记录时,会以name和当前时间为参数新建一个文件(name.now.log)用于记录。
|
13
|
+
:param encoding: 记录文件的编码格式,默认为 "utf-8"
|
14
|
+
:param errors: 当编码模式失败时的处理方式,默认为 "ignore"
|
15
|
+
:param raw: 记录带ANSI标记的原始内容,还是记录纯文本内容,默认为True,即记录带ANSI标记的原始内容。
|
16
|
+
"""
|
17
|
+
|
18
|
+
_esc_regx = re.compile("\x1b\\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
|
19
|
+
|
20
|
+
def __init__(self, name, mode = 'a', encoding = "utf-8", errors = "ignore", raw = False):
|
21
|
+
self._name = name
|
22
|
+
self._enabled = False
|
23
|
+
self._raw = raw
|
24
|
+
self.mode = mode
|
25
|
+
self._encoding = encoding
|
26
|
+
self._errors = errors
|
27
|
+
self._lock = threading.RLock()
|
28
|
+
self._stream = None
|
29
|
+
|
30
|
+
self._queue = SimpleQueue()
|
31
|
+
|
32
|
+
@property
|
33
|
+
def name(self):
|
34
|
+
"记录器名称,仅在创建时设置,过程中只读"
|
35
|
+
return self._name
|
36
|
+
|
37
|
+
@property
|
38
|
+
def enabled(self):
|
39
|
+
"""
|
40
|
+
使能属性。
|
41
|
+
从false切换到true时,会打开文件,创建后台线程进行记录。
|
42
|
+
从true切换到false时,会终止后台记录线程,并关闭记录文件。
|
43
|
+
"""
|
44
|
+
return self._enabled
|
45
|
+
|
46
|
+
@enabled.setter
|
47
|
+
def enabled(self, enabled):
|
48
|
+
if self._enabled != enabled:
|
49
|
+
if enabled:
|
50
|
+
mode = "a"
|
51
|
+
|
52
|
+
if self._mode in ("a", "w"):
|
53
|
+
filename = f"{self.name}.log"
|
54
|
+
mode = self._mode
|
55
|
+
elif self._mode == "n":
|
56
|
+
now = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
|
57
|
+
filename = f"{self.name}.{now}.log"
|
58
|
+
|
59
|
+
filename = os.path.abspath(filename)
|
60
|
+
self._stream = open(filename, mode = mode, encoding = self._encoding, errors = self._errors)
|
61
|
+
self._thread = t = threading.Thread(target=self._monitor)
|
62
|
+
t.daemon = True
|
63
|
+
t.start()
|
64
|
+
|
65
|
+
else:
|
66
|
+
self._queue.put_nowait(None)
|
67
|
+
self._thread.join()
|
68
|
+
self._thread = None
|
69
|
+
self._closeFile()
|
70
|
+
|
71
|
+
self._enabled = enabled
|
72
|
+
|
73
|
+
@property
|
74
|
+
def raw(self):
|
75
|
+
"属性,设置和获取是否记录带有ANSI标记的原始记录"
|
76
|
+
return self._raw
|
77
|
+
|
78
|
+
@raw.setter
|
79
|
+
def raw(self, val: bool):
|
80
|
+
self._raw = val
|
81
|
+
|
82
|
+
@property
|
83
|
+
def mode(self):
|
84
|
+
"属性,记录器模式,可为 a, w, n"
|
85
|
+
return self._mode
|
86
|
+
|
87
|
+
@mode.setter
|
88
|
+
def mode(self, value):
|
89
|
+
if value in ("a", "w", "n"):
|
90
|
+
self._mode = value
|
91
|
+
|
92
|
+
def _closeFile(self):
|
93
|
+
"""
|
94
|
+
Closes the stream.
|
95
|
+
"""
|
96
|
+
self._lock.acquire()
|
97
|
+
try:
|
98
|
+
if self._stream:
|
99
|
+
try:
|
100
|
+
self._stream.flush()
|
101
|
+
finally:
|
102
|
+
stream = self._stream
|
103
|
+
self._stream = None
|
104
|
+
stream.close()
|
105
|
+
finally:
|
106
|
+
self._lock.release()
|
107
|
+
|
108
|
+
def log(self, msg):
|
109
|
+
"""
|
110
|
+
向记录器记录信息。记录的信息会通过队列发送到独立的记录线程。
|
111
|
+
当记录器未使能时,使用该函数调用也不会记录。
|
112
|
+
|
113
|
+
:param msg: 要记录的信息
|
114
|
+
"""
|
115
|
+
if self._enabled:
|
116
|
+
self._queue.put_nowait(msg)
|
117
|
+
|
118
|
+
def _monitor(self):
|
119
|
+
"""
|
120
|
+
Monitor the queue for records, and ask the handler
|
121
|
+
to deal with them.
|
122
|
+
|
123
|
+
This method runs on a separate, internal thread.
|
124
|
+
The thread will terminate if it sees a sentinel object in the queue.
|
125
|
+
"""
|
126
|
+
newline = True
|
127
|
+
while True:
|
128
|
+
try:
|
129
|
+
data = self._queue.get(block = True)
|
130
|
+
if data:
|
131
|
+
self._lock.acquire()
|
132
|
+
|
133
|
+
if newline:
|
134
|
+
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
135
|
+
header = f"{now} {self._name}: "
|
136
|
+
self._stream.write(header)
|
137
|
+
newline = False
|
138
|
+
|
139
|
+
if data.endswith("\n"):
|
140
|
+
data = data.rstrip("\n").rstrip("\r") + "\n"
|
141
|
+
newline = True
|
142
|
+
|
143
|
+
if not self._raw:
|
144
|
+
data = self._esc_regx.sub("", data)
|
145
|
+
|
146
|
+
self._stream.write(data)
|
147
|
+
|
148
|
+
self._stream.flush()
|
149
|
+
self._lock.release()
|
150
|
+
|
151
|
+
else:
|
152
|
+
break
|
153
|
+
except Empty:
|
154
|
+
break
|
pymud/objects.py
CHANGED
@@ -5,7 +5,6 @@ MUD会话(session)中, 支持的对象列表
|
|
5
5
|
import asyncio, logging, re
|
6
6
|
from collections.abc import Iterable
|
7
7
|
from collections import namedtuple
|
8
|
-
import functools
|
9
8
|
from typing import Any
|
10
9
|
from .settings import Settings
|
11
10
|
|
@@ -991,3 +990,4 @@ class SimpleTimer(Timer):
|
|
991
990
|
|
992
991
|
def __detailed__(self) -> str:
|
993
992
|
return f'<{self.__class__.__name__}> id = "{self.id}" group = "{self.group}" enabled = {self.enabled} timeout = {self.timeout} code = "{self._code}"'
|
993
|
+
|
pymud/pymud.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
import asyncio, functools, re, logging, math, json, os, webbrowser
|
1
|
+
import asyncio, functools, re, logging, math, json, os, webbrowser, threading
|
2
|
+
from datetime import datetime
|
2
3
|
import importlib.util
|
3
|
-
from prompt_toolkit.shortcuts import set_title
|
4
|
+
from prompt_toolkit.shortcuts import set_title, radiolist_dialog
|
4
5
|
from prompt_toolkit.output import ColorDepth
|
5
6
|
from prompt_toolkit.clipboard.pyperclip import PyperclipClipboard
|
6
7
|
from prompt_toolkit import HTML
|
@@ -8,7 +9,7 @@ from prompt_toolkit.buffer import Buffer
|
|
8
9
|
from prompt_toolkit.application import Application
|
9
10
|
from prompt_toolkit.filters import Condition
|
10
11
|
from prompt_toolkit.key_binding import KeyBindings
|
11
|
-
from prompt_toolkit.layout import ConditionalContainer, Float, VSplit, HSplit, Window, WindowAlign
|
12
|
+
from prompt_toolkit.layout import ConditionalContainer, Float, VSplit, HSplit, Window, WindowAlign, ScrollbarMargin, NumberedMargin
|
12
13
|
from prompt_toolkit.layout.layout import Layout
|
13
14
|
from prompt_toolkit.layout.controls import FormattedTextControl
|
14
15
|
from prompt_toolkit.layout.dimension import D
|
@@ -38,7 +39,7 @@ from .objects import CodeBlock
|
|
38
39
|
from .extras import MudFormatProcessor, SessionBuffer, EasternMenuContainer, VSplitWindow, SessionBufferControl, DotDict, Plugin
|
39
40
|
from .session import Session
|
40
41
|
from .settings import Settings
|
41
|
-
from .dialogs import MessageDialog, WelcomeDialog, QueryDialog, NewSessionDialog
|
42
|
+
from .dialogs import MessageDialog, WelcomeDialog, QueryDialog, NewSessionDialog, LogSelectionDialog
|
42
43
|
|
43
44
|
from enum import Enum
|
44
45
|
|
@@ -140,6 +141,12 @@ class PyMudApp:
|
|
140
141
|
set_title("{} {}".format(Settings.__appname__, Settings.__version__))
|
141
142
|
self.set_status(Settings.text["welcome"])
|
142
143
|
|
144
|
+
self.loggers = dict() # 所有记录字典
|
145
|
+
self.showLog = False # 是否显示记录页
|
146
|
+
self.logFileShown = '' # 记录页显示的记录文件名
|
147
|
+
self.logSessionBuffer = SessionBuffer()
|
148
|
+
self.logSessionBuffer.name = "LOGBUFFER"
|
149
|
+
|
143
150
|
self.load_plugins()
|
144
151
|
|
145
152
|
def initUI(self):
|
@@ -193,6 +200,7 @@ class PyMudApp:
|
|
193
200
|
width = D(preferred = Settings.client["naws_width"]),
|
194
201
|
height = D(preferred = Settings.client["naws_height"]),
|
195
202
|
wrap_lines=Condition(lambda: is_true(self.wrap_lines)),
|
203
|
+
#left_margins=[NumberedMargin()],
|
196
204
|
#right_margins=[ScrollbarMargin(True)],
|
197
205
|
style="class:text-area"
|
198
206
|
)
|
@@ -363,16 +371,26 @@ class PyMudApp:
|
|
363
371
|
menu.children.append(sub)
|
364
372
|
menus.append(menu)
|
365
373
|
|
374
|
+
menus.append(MenuItem("-", disabled=True))
|
375
|
+
menus.append(MenuItem(Settings.text["show_log"], handler = self.show_logSelectDialog))
|
366
376
|
menus.append(MenuItem("-", disabled=True))
|
367
377
|
menus.append(MenuItem(Settings.text["exit"], handler=self.act_exit))
|
368
378
|
|
369
379
|
return menus
|
370
380
|
|
381
|
+
def invalidate(self):
|
382
|
+
"刷新显示界面"
|
383
|
+
self.app.invalidate()
|
384
|
+
|
371
385
|
def scroll(self, lines = 1):
|
372
386
|
"内容滚动指定行数,小于0为向上滚动,大于0为向下滚动"
|
373
387
|
if self.current_session:
|
374
388
|
s = self.current_session
|
375
389
|
b = s.buffer
|
390
|
+
elif self.showLog:
|
391
|
+
b = self.logSessionBuffer
|
392
|
+
|
393
|
+
if isinstance(b, Buffer):
|
376
394
|
if lines < 0:
|
377
395
|
b.cursor_up(-1 * lines)
|
378
396
|
elif lines > 0:
|
@@ -439,11 +457,22 @@ class PyMudApp:
|
|
439
457
|
new_key = keys[idx+1]
|
440
458
|
self.activate_session(new_key)
|
441
459
|
|
460
|
+
elif (idx == count -1) and self.showLog:
|
461
|
+
self.showLogInTab()
|
462
|
+
|
442
463
|
elif event.key_sequence[-1].key == Keys.ControlLeft:
|
443
464
|
if idx > 0:
|
444
465
|
new_key = keys[idx-1]
|
445
466
|
self.activate_session(new_key)
|
446
467
|
|
468
|
+
else:
|
469
|
+
if self.showLog:
|
470
|
+
if event.key_sequence[-1].key == Keys.ControlLeft:
|
471
|
+
keys = list(self.sessions.keys())
|
472
|
+
if len(keys) > 0:
|
473
|
+
new_key = keys[-1]
|
474
|
+
self.activate_session(new_key)
|
475
|
+
|
447
476
|
def toggle_mousesupport(self, event: KeyPressEvent):
|
448
477
|
"""快捷键F2: 切换鼠标支持状态。用于远程连接时本地复制命令执行操作"""
|
449
478
|
self._mouse_support = not self._mouse_support
|
@@ -482,8 +511,8 @@ class PyMudApp:
|
|
482
511
|
selection = line_plain[start:end]
|
483
512
|
self.app.clipboard.set_text(selection)
|
484
513
|
self.set_status("已复制:{}".format(selection))
|
485
|
-
|
486
|
-
|
514
|
+
if self.current_session:
|
515
|
+
self.current_session.setVariable("%copy", selection)
|
487
516
|
else:
|
488
517
|
# 多行只认行
|
489
518
|
lines = []
|
@@ -494,8 +523,9 @@ class PyMudApp:
|
|
494
523
|
|
495
524
|
self.app.clipboard.set_text("\n".join(lines))
|
496
525
|
self.set_status("已复制:行数{}".format(1 + erow - srow))
|
497
|
-
|
498
|
-
self.current_session
|
526
|
+
|
527
|
+
if self.current_session:
|
528
|
+
self.current_session.setVariable("%copy", "\n".join(lines))
|
499
529
|
|
500
530
|
else:
|
501
531
|
# Control-R 复制带有ANSI标记的原始内容(对应字符关系会不正确,因此RAW复制时自动整行复制)
|
@@ -503,15 +533,18 @@ class PyMudApp:
|
|
503
533
|
line = b.document.current_line
|
504
534
|
self.app.clipboard.set_text(line)
|
505
535
|
self.set_status("已复制:{}".format(line))
|
506
|
-
|
507
|
-
self.current_session
|
536
|
+
|
537
|
+
if self.current_session:
|
538
|
+
self.current_session.setVariable("%copy", line)
|
508
539
|
|
509
540
|
else:
|
510
541
|
lines = b.document.lines[srow:erow+1]
|
511
542
|
copy_raw_text = "".join(lines)
|
512
543
|
self.app.clipboard.set_text(copy_raw_text)
|
513
544
|
self.set_status("已复制:行数{}".format(1 + erow - srow))
|
514
|
-
|
545
|
+
|
546
|
+
if self.current_session:
|
547
|
+
self.current_session.setVariable("%copy", copy_raw_text)
|
515
548
|
|
516
549
|
# data = self.consoleView.buffer.copy_selection()
|
517
550
|
# self.app.clipboard.set_data(data)
|
@@ -552,6 +585,52 @@ class PyMudApp:
|
|
552
585
|
|
553
586
|
return result
|
554
587
|
|
588
|
+
def show_logSelectDialog(self):
|
589
|
+
async def coroutine():
|
590
|
+
head_line = " {}{}{}".format('记录文件名'.ljust(15), '文件大小'.rjust(16), '最后修改时间'.center(17))
|
591
|
+
|
592
|
+
log_list = list()
|
593
|
+
files = [f for f in os.listdir('.') if os.path.isfile(f) and f.endswith('.log')]
|
594
|
+
for file in files:
|
595
|
+
filename = os.path.basename(file).ljust(20)
|
596
|
+
filesize = f"{os.path.getsize(file):,} Bytes".rjust(20)
|
597
|
+
# ctime = datetime.fromtimestamp(os.path.getctime(file)).strftime('%Y-%m-%d %H:%M:%S').rjust(23)
|
598
|
+
mtime = datetime.fromtimestamp(os.path.getmtime(file)).strftime('%Y-%m-%d %H:%M:%S').rjust(23)
|
599
|
+
|
600
|
+
file_display_line = "{}{}{}".format(filename, filesize, mtime)
|
601
|
+
log_list.append((file, file_display_line))
|
602
|
+
|
603
|
+
dialog = LogSelectionDialog(
|
604
|
+
text = head_line,
|
605
|
+
values = log_list
|
606
|
+
)
|
607
|
+
|
608
|
+
result = await self.show_dialog_as_float(dialog)
|
609
|
+
|
610
|
+
if result:
|
611
|
+
self.logFileShown = result
|
612
|
+
self.showLogInTab()
|
613
|
+
|
614
|
+
asyncio.ensure_future(coroutine())
|
615
|
+
|
616
|
+
def showLogInTab(self):
|
617
|
+
"在记录也显示LOG记录"
|
618
|
+
self.current_session = None
|
619
|
+
self.showLog = True
|
620
|
+
|
621
|
+
if self.logFileShown:
|
622
|
+
filename = os.path.abspath(self.logFileShown)
|
623
|
+
if os.path.exists(filename):
|
624
|
+
lock = threading.RLock()
|
625
|
+
lock.acquire()
|
626
|
+
with open(filename, 'r', encoding = 'utf-8', errors = 'ignore') as file:
|
627
|
+
self.logSessionBuffer._set_text(file.read())
|
628
|
+
lock.release()
|
629
|
+
|
630
|
+
self.logSessionBuffer.cursor_position = len(self.logSessionBuffer.text)
|
631
|
+
self.consoleView.buffer = self.logSessionBuffer
|
632
|
+
self.app.invalidate()
|
633
|
+
|
555
634
|
def activate_session(self, key):
|
556
635
|
"激活指定名称的session,并将该session设置为当前session"
|
557
636
|
session = self.sessions.get(key, None)
|
@@ -627,9 +706,22 @@ class PyMudApp:
|
|
627
706
|
b.exit_selection()
|
628
707
|
b.cursor_position = len(b.text)
|
629
708
|
|
709
|
+
elif self.showLog:
|
710
|
+
b = self.logSessionBuffer
|
711
|
+
b.exit_selection()
|
712
|
+
b.cursor_position = len(b.text)
|
713
|
+
|
630
714
|
def act_close_session(self):
|
631
715
|
"菜单: 关闭当前会话"
|
632
|
-
self.
|
716
|
+
if self.current_session:
|
717
|
+
self.close_session()
|
718
|
+
|
719
|
+
elif self.showLog:
|
720
|
+
self.showLog = False
|
721
|
+
self.logSessionBuffer.text = ""
|
722
|
+
if len(self.sessions.keys()) > 0:
|
723
|
+
new_sess = list(self.sessions.keys())[0]
|
724
|
+
self.activate_session(new_sess)
|
633
725
|
|
634
726
|
def act_echoinput(self):
|
635
727
|
"菜单: 显示/隐藏输入指令"
|
@@ -722,12 +814,21 @@ class PyMudApp:
|
|
722
814
|
def btn_title_clicked(self, name, mouse_event: MouseEvent):
|
723
815
|
"顶部会话标签点击切换鼠标事件"
|
724
816
|
if mouse_event.event_type == MouseEventType.MOUSE_UP:
|
725
|
-
|
817
|
+
if name == '[LOG]':
|
818
|
+
self.showLogInTab()
|
819
|
+
else:
|
820
|
+
self.activate_session(name)
|
726
821
|
|
727
822
|
def get_frame_title(self):
|
728
823
|
"顶部会话标题选项卡"
|
729
824
|
if len(self.sessions.keys()) == 0:
|
730
|
-
|
825
|
+
if not self.showLog:
|
826
|
+
return Settings.__appname__ + " " + Settings.__version__
|
827
|
+
else:
|
828
|
+
if self.logFileShown:
|
829
|
+
return f'[LOG] {self.logFileShown}'
|
830
|
+
else:
|
831
|
+
return f'[LOG]'
|
731
832
|
|
732
833
|
title_formatted_list = []
|
733
834
|
for key, session in self.sessions.items():
|
@@ -746,6 +847,17 @@ class PyMudApp:
|
|
746
847
|
title_formatted_list.append((style, key, functools.partial(self.btn_title_clicked, key)))
|
747
848
|
title_formatted_list.append(("", " | "))
|
748
849
|
|
850
|
+
if self.showLog:
|
851
|
+
if self.current_session is None:
|
852
|
+
style = style = Settings.styles["selected"]
|
853
|
+
else:
|
854
|
+
style = Settings.styles["normal"]
|
855
|
+
|
856
|
+
title = f'[LOG] {self.logFileShown}' if self.logFileShown else f'[LOG]'
|
857
|
+
|
858
|
+
title_formatted_list.append((style, title, functools.partial(self.btn_title_clicked, '[LOG]')))
|
859
|
+
title_formatted_list.append(("", " | "))
|
860
|
+
|
749
861
|
return title_formatted_list[:-1]
|
750
862
|
|
751
863
|
def get_statusbar_text(self):
|
@@ -880,13 +992,20 @@ class PyMudApp:
|
|
880
992
|
self.current_session.writeline("")
|
881
993
|
else:
|
882
994
|
try:
|
995
|
+
self.current_session.log.log(f"命令行键入: {cmd_line}\n")
|
996
|
+
|
883
997
|
cb = CodeBlock(cmd_line)
|
884
998
|
cb.execute(self.current_session)
|
885
999
|
except Exception as e:
|
886
1000
|
self.current_session.warning(e)
|
887
1001
|
self.current_session.exec_command(cmd_line)
|
888
1002
|
else:
|
889
|
-
|
1003
|
+
if cmd_line == "#exit":
|
1004
|
+
self.act_exit()
|
1005
|
+
elif (cmd_line == "#close") and self.showLog:
|
1006
|
+
self.act_close_session()
|
1007
|
+
else:
|
1008
|
+
self.set_status("当前没有正在运行的session.")
|
890
1009
|
|
891
1010
|
# 配置:命令行内容保留
|
892
1011
|
if Settings.client["remain_last_input"]:
|
@@ -1013,7 +1132,7 @@ class PyMudApp:
|
|
1013
1132
|
self._plugins[plugin.name] = plugin
|
1014
1133
|
plugin.onAppInit(self)
|
1015
1134
|
except Exception as e:
|
1016
|
-
self.set_status(f"文件: {plugins_dir}
|
1135
|
+
self.set_status(f"文件: {plugins_dir}\\{file} 不是一个合法的插件文件,加载错误,信息为: {e}")
|
1017
1136
|
|
1018
1137
|
# 然后加载当前目录下的插件
|
1019
1138
|
current_dir = os.path.abspath(".")
|
@@ -1028,8 +1147,7 @@ class PyMudApp:
|
|
1028
1147
|
self._plugins[plugin.name] = plugin
|
1029
1148
|
plugin.onAppInit(self)
|
1030
1149
|
except Exception as e:
|
1031
|
-
self.set_status(f"文件: {plugins_dir}
|
1032
|
-
|
1150
|
+
self.set_status(f"文件: {plugins_dir}\\{file} 不是一个合法的插件文件. 加载错误,信息为: {e}")
|
1033
1151
|
|
1034
1152
|
def reload_plugin(self, plugin: Plugin):
|
1035
1153
|
"重新加载指定插件"
|
pymud/session.py
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
import asyncio, logging, re, math, os, pickle, datetime, importlib, importlib.util, sysconfig
|
2
2
|
from collections.abc import Iterable
|
3
3
|
from collections import OrderedDict
|
4
|
+
import logging, queue
|
5
|
+
from logging import FileHandler
|
6
|
+
from logging.handlers import QueueHandler, QueueListener
|
4
7
|
|
8
|
+
from .logger import Logger
|
5
9
|
from .extras import SessionBuffer, DotDict, Plugin
|
6
10
|
from .protocol import MudClientProtocol
|
7
11
|
from .objects import Trigger, Alias, Command, Timer, SimpleAlias, SimpleTrigger, SimpleTimer, GMCPTrigger, CodeBlock, CodeLine
|
@@ -73,6 +77,8 @@ class Session:
|
|
73
77
|
"py", # 直接执行python语句
|
74
78
|
|
75
79
|
"all", # 所有会话执行
|
80
|
+
|
81
|
+
"log", # 记录处置
|
76
82
|
)
|
77
83
|
|
78
84
|
_commands_alias = {
|
@@ -96,7 +102,8 @@ class Session:
|
|
96
102
|
def __init__(self, app, name, host, port, encoding = None, after_connect = None, loop = None, **kwargs):
|
97
103
|
self.pyversion = sysconfig.get_python_version()
|
98
104
|
self.loop = loop or asyncio.get_running_loop()
|
99
|
-
self.
|
105
|
+
self.syslog = logging.getLogger("pymud.Session")
|
106
|
+
|
100
107
|
self.application = app
|
101
108
|
self.name = name
|
102
109
|
self._transport = None
|
@@ -109,6 +116,9 @@ class Session:
|
|
109
116
|
self._events["connected"] = None
|
110
117
|
self._events["disconnected"] = None
|
111
118
|
|
119
|
+
self._loggers = dict()
|
120
|
+
self.log = self.getLogger(name)
|
121
|
+
|
112
122
|
self._auto_script = kwargs.get("scripts", None)
|
113
123
|
|
114
124
|
self._cmds_handler = dict() # 支持的命令的处理函数字典
|
@@ -159,7 +169,6 @@ class Session:
|
|
159
169
|
self.warning(f"自动从{file}中加载变量失败,错误消息为: {e}")
|
160
170
|
|
161
171
|
|
162
|
-
|
163
172
|
if self._auto_script:
|
164
173
|
self.info(f"即将自动加载以下模块:{self._auto_script}")
|
165
174
|
self.load_module(self._auto_script)
|
@@ -340,6 +349,33 @@ class Session:
|
|
340
349
|
def event_disconnected(self, event):
|
341
350
|
self._events["disconnected"] = event
|
342
351
|
|
352
|
+
def getLogger(self, name, mode = 'a', encoding = 'utf-8', encoding_errors = 'ignore', raw = False) -> Logger:
|
353
|
+
"""
|
354
|
+
根据指定名称和参数获取并返回一个记录器。若指定名称不存在,则创建一个该名称记录器。
|
355
|
+
|
356
|
+
:param name: 指定的记录器名称
|
357
|
+
:param mode: 记录器的模式,可接受值为 a, w, n。 具体请参见 Logger 对象中 mode 参数
|
358
|
+
:param encoding: 记录文件的编码格式
|
359
|
+
:param encoding_errors: 编码错误的处理方式
|
360
|
+
:param raw: 是否以带ANSI标记的原始格式进行记录
|
361
|
+
|
362
|
+
:return 指定名称的记录器 Logger 对象
|
363
|
+
"""
|
364
|
+
if name not in self.application.loggers.keys():
|
365
|
+
logger = Logger(name, mode, encoding, encoding_errors, raw)
|
366
|
+
self._loggers[name] = logger
|
367
|
+
self.application.loggers[name] = logger
|
368
|
+
|
369
|
+
else:
|
370
|
+
if name not in self._loggers.keys():
|
371
|
+
self.warning(f"其它会话中已存在一个名为 {name} 的记录器,将直接返回该记录器")
|
372
|
+
|
373
|
+
logger = self.application.loggers[name]
|
374
|
+
logger.mode = mode
|
375
|
+
logger.raw = raw
|
376
|
+
|
377
|
+
return logger
|
378
|
+
|
343
379
|
@property
|
344
380
|
def modules(self) -> OrderedDict:
|
345
381
|
"""
|
@@ -484,6 +520,7 @@ class Session:
|
|
484
520
|
:param newline: 是否额外增加换行符
|
485
521
|
"""
|
486
522
|
self.buffer.insert_text(data)
|
523
|
+
self.log.log(data)
|
487
524
|
|
488
525
|
if len(data) > 0 and (data[-1] == "\n"):
|
489
526
|
self._line_count += 1
|
@@ -491,6 +528,7 @@ class Session:
|
|
491
528
|
if newline:
|
492
529
|
self.buffer.insert_text(self.newline_cli)
|
493
530
|
self._line_count += 1
|
531
|
+
self.log.log(self.newline_cli)
|
494
532
|
|
495
533
|
def clear_half(self):
|
496
534
|
"""
|
@@ -520,7 +558,7 @@ class Session:
|
|
520
558
|
if self.connected:
|
521
559
|
self._transport.write_eof()
|
522
560
|
self.state = "DISCONNECTED"
|
523
|
-
self.
|
561
|
+
self.syslog.info(f"服务器断开连接! {self._protocol.__repr__}")
|
524
562
|
|
525
563
|
def feed_gmcp(self, name, value) -> None:
|
526
564
|
"""
|
@@ -703,19 +741,22 @@ class Session:
|
|
703
741
|
if self.seperator in line:
|
704
742
|
lines = line.split(self.seperator)
|
705
743
|
for ln in lines:
|
744
|
+
if Settings.client["echo_input"]:
|
745
|
+
self.writetobuffer(f"\x1b[32m{ln}\x1b[0m")
|
746
|
+
else:
|
747
|
+
self.log.log(f"\x1b[32m{ln}\x1b[0m\n")
|
748
|
+
|
706
749
|
cmd = ln + self.newline
|
707
750
|
self.write(cmd.encode(self.encoding, Settings.server["encoding_errors"]))
|
708
751
|
|
709
|
-
if Settings.client["echo_input"]:
|
710
|
-
self.writetobuffer(f"\x1b[32m{cmd}\x1b[0m")
|
711
|
-
|
712
752
|
else:
|
753
|
+
if Settings.client["echo_input"]:
|
754
|
+
self.writetobuffer(f"\x1b[32m{line}\x1b[0m")
|
755
|
+
else:
|
756
|
+
self.log.log(f"\x1b[32m{line}\x1b[0m\n")
|
757
|
+
|
713
758
|
cmd = line + self.newline
|
714
759
|
self.write(cmd.encode(self.encoding, Settings.server["encoding_errors"]))
|
715
|
-
|
716
|
-
#if Settings.client["echo_input"]:
|
717
|
-
if Settings.client["echo_input"] and (len(cmd) > len(self.newline)): # 修改2023-12-3, 向服务器发送空回车时,不回显
|
718
|
-
self.writetobuffer(f"\x1b[32m{cmd}\x1b[0m")
|
719
760
|
|
720
761
|
def exec(self, cmd: str, name = None, *args, **kwargs):
|
721
762
|
"""
|
@@ -1604,7 +1645,8 @@ class Session:
|
|
1604
1645
|
- #session
|
1605
1646
|
'''
|
1606
1647
|
|
1607
|
-
self.application.close_session()
|
1648
|
+
#self.application.close_session()
|
1649
|
+
self.application.act_close_session()
|
1608
1650
|
|
1609
1651
|
async def handle_wait(self, code: CodeLine = None, *args, **kwargs):
|
1610
1652
|
'''
|
@@ -2316,7 +2358,12 @@ class Session:
|
|
2316
2358
|
else:
|
2317
2359
|
mod = self._modules[module_name]["module"]
|
2318
2360
|
config = self._modules[module_name]["config"]
|
2319
|
-
if config:
|
2361
|
+
if config:
|
2362
|
+
if hasattr(config, "unload"):
|
2363
|
+
unload = getattr(config, "unload", None)
|
2364
|
+
if callable(unload):
|
2365
|
+
unload(config)
|
2366
|
+
del config
|
2320
2367
|
mod = importlib.reload(mod)
|
2321
2368
|
if hasattr(mod, 'Configuration'):
|
2322
2369
|
config = mod.Configuration(self)
|
@@ -2832,3 +2879,94 @@ class Session:
|
|
2832
2879
|
:param style: 要输出信息的格式(ANSI), 默认为 ERR_STYLE, \x1b[31m
|
2833
2880
|
"""
|
2834
2881
|
self.info2(msg, title, style)
|
2882
|
+
|
2883
|
+
def handle_log(self, code: CodeLine = None, *args, **kwargs):
|
2884
|
+
'''
|
2885
|
+
嵌入命令 #log 的执行函数,控制当前会话的记录状态。
|
2886
|
+
该函数不应该在代码中直接调用。
|
2887
|
+
|
2888
|
+
使用:
|
2889
|
+
- #log : 显示所有记录器的状态情况
|
2890
|
+
- #log start [logger-name] [-a|-w|-n] [-r] : 启动一个记录器
|
2891
|
+
|
2892
|
+
参数:
|
2893
|
+
- :logger-name: 记录器名称。当不指定时,选择名称为会话名称的记录器(会话默认记录器)
|
2894
|
+
- :-a|-w|-n: 记录器模式选择。 -a 为添加模式(未指定时默认值),在原记录文件后端添加; -w 为覆写模式,清空原记录文件并重新记录; -n 为新建模式,以名称和当前时间为参数,使用 name.now.log 形式创建新的记录文件
|
2895
|
+
- :-r: 指定记录器是否使用 raw 模式
|
2896
|
+
|
2897
|
+
- #log stop [logger-name] : 停止一个记录器
|
2898
|
+
|
2899
|
+
参数:
|
2900
|
+
- :logger-name: 记录器名称。当不指定时,选择名称为会话名称的记录器(会话默认记录器)
|
2901
|
+
|
2902
|
+
- #log show [loggerFile]: 显示全部日志记录或指定记录文件
|
2903
|
+
|
2904
|
+
参数:
|
2905
|
+
- :loggerFile: 要显示的记录文件名称。当不指定时,弹出对话框列出当前目录下所有记录文件
|
2906
|
+
|
2907
|
+
示例:
|
2908
|
+
- ``#log`` : 在当前会话窗口列出所有记录器状态
|
2909
|
+
- ``#log start`` : 启动本会话默认记录器(记录器名为会话名)。该记录器以纯文本模式,将后续所有屏幕输出、键盘键入、命令输入等记录到 name.log 文件的后端
|
2910
|
+
- ``#log start -r`` : 启动本会话默认记录器。该记录器以raw模式,将后续所有屏幕输出、键盘键入、命令输入等记录到 name.log 文件的后端
|
2911
|
+
- ``#log start chat`` : 启动名为 chat 的记录器。该记录器以纯文本模式,记录代码中调用过该记录器 .log 进行记录的信息
|
2912
|
+
- ``#log stop`` : 停止本会话默认记录器(记录器名为会话名)。
|
2913
|
+
|
2914
|
+
注意:
|
2915
|
+
- 记录器文件模式(-a|-w|-n)在修改后,只有在下一次该记录器启动时才会生效
|
2916
|
+
- 记录器记录模式(-r)在修改后立即生效
|
2917
|
+
'''
|
2918
|
+
|
2919
|
+
args = list()
|
2920
|
+
if isinstance(code, CodeLine):
|
2921
|
+
args = code.code[2:]
|
2922
|
+
|
2923
|
+
if len(args) == 0:
|
2924
|
+
session_loggers = set(self._loggers.keys())
|
2925
|
+
app_loggers = set(self.application.loggers.keys()).difference(session_loggers)
|
2926
|
+
self.info("本会话中的记录器情况:")
|
2927
|
+
for name in session_loggers:
|
2928
|
+
logger = self.application.loggers[name]
|
2929
|
+
self.info(f"记录器 {logger.name}, 当前状态: {'开启' if logger.enabled else '关闭'}, 文件模式: {logger.mode}, 记录模式: {'ANSI' if logger.raw else '纯文本'}")
|
2930
|
+
|
2931
|
+
if len(app_loggers) > 0:
|
2932
|
+
self.info("本应用其他会话中的记录器情况:")
|
2933
|
+
for name in app_loggers:
|
2934
|
+
logger = self.application.loggers[name]
|
2935
|
+
self.info(f"记录器 {logger.name}, 当前状态: {'开启' if logger.enabled else '关闭'}, 文件模式: {logger.mode}, 记录模式: {'ANSI' if logger.raw else '纯文本'}")
|
2936
|
+
|
2937
|
+
else:
|
2938
|
+
name = self.name
|
2939
|
+
if len(args) > 1 and not args[1].startswith('-'):
|
2940
|
+
name = args[1]
|
2941
|
+
|
2942
|
+
if (args[0] == "start"):
|
2943
|
+
if "-n" in args:
|
2944
|
+
mode = "n"
|
2945
|
+
mode_name = '新建'
|
2946
|
+
elif "-w" in args:
|
2947
|
+
mode = "w"
|
2948
|
+
mode_name = '覆写'
|
2949
|
+
else:
|
2950
|
+
mode = "a"
|
2951
|
+
mode_name = '添加'
|
2952
|
+
|
2953
|
+
raw = True if "-r" in args else False
|
2954
|
+
raw_name = '原始ANSI' if raw else '纯文本'
|
2955
|
+
|
2956
|
+
logger = self.getLogger(name = name, mode = mode, raw = raw)
|
2957
|
+
logger.enabled = True
|
2958
|
+
|
2959
|
+
self.info(f"{datetime.datetime.now()}: 记录器{name}以{mode_name}文件模式以及{raw_name}记录模式开启。")
|
2960
|
+
|
2961
|
+
elif (args[0] == "stop"):
|
2962
|
+
self.info(f"{datetime.datetime.now()}: 记录器{name}记录已关闭。")
|
2963
|
+
self.log.enabled = False
|
2964
|
+
|
2965
|
+
elif (args[0] == "show"):
|
2966
|
+
if len(args) > 1 and not args[1].startswith('-'):
|
2967
|
+
file = args[1]
|
2968
|
+
self.application.logFileShown = file
|
2969
|
+
self.application.showLogInTab()
|
2970
|
+
else:
|
2971
|
+
self.application.show_logSelectDialog()
|
2972
|
+
|
pymud/settings.py
CHANGED
@@ -11,9 +11,9 @@ class Settings:
|
|
11
11
|
"APP 名称, 默认PYMUD"
|
12
12
|
__appdesc__ = "a MUD client written in Python"
|
13
13
|
"APP 简要描述"
|
14
|
-
__version__ = "0.
|
14
|
+
__version__ = "0.20.0"
|
15
15
|
"APP 当前版本"
|
16
|
-
__release__ = "2024-
|
16
|
+
__release__ = "2024-08-14"
|
17
17
|
"APP 当前版本发布日期"
|
18
18
|
__author__ = "本牛(newstart)@北侠"
|
19
19
|
"APP 作者"
|
@@ -79,6 +79,7 @@ class Settings:
|
|
79
79
|
"welcome" : "欢迎使用PYMUD客户端 - 北大侠客行,最好的中文MUD游戏",
|
80
80
|
"world" : "世界",
|
81
81
|
"new_session" : "创建新会话...",
|
82
|
+
"show_log" : "显示记录信息",
|
82
83
|
"exit" : "退出",
|
83
84
|
"session" : "会话",
|
84
85
|
"connect" : "连接/重新连接",
|
@@ -88,7 +89,7 @@ class Settings:
|
|
88
89
|
"copy" : "复制(纯文本)",
|
89
90
|
"copyraw" : "复制(ANSI)",
|
90
91
|
"clearsession" : "清空会话内容",
|
91
|
-
"closesession" : "
|
92
|
+
"closesession" : "关闭当前页面",
|
92
93
|
"autoreconnect" : "打开/关闭自动重连",
|
93
94
|
"loadconfig" : "加载脚本配置",
|
94
95
|
"reloadconfig" : "重新加载脚本配置",
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pymud
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.20.0a1
|
4
4
|
Summary: a MUD Client written in Python
|
5
5
|
Author-email: "newstart@pkuxkx" <crapex@crapex.cc>
|
6
6
|
Maintainer-email: "newstart@pkuxkx" <crapex@crapex.cc>
|
@@ -970,3 +970,9 @@ Requires-Dist: prompt-toolkit
|
|
970
970
|
+ 功能调整: #var, #global 指令中,现在对字符串会先使用 eval 转换类型,转换失败时使用 str 类型。例如, #var myvar 1 时,myvar类型将为int
|
971
971
|
+ 功能调整: 变量替代时,会自动实现类型转化,当被替代变量值为非 str 类型时不会再报错
|
972
972
|
+ 问题修复: 修复之前从后向前选择时,无法复制的问题
|
973
|
+
|
974
|
+
### 0.19.5 (2024-08-XX)
|
975
|
+
+ 功能调整: 恢复在__init__.py中增加PyMudApp的导出,可以恢复使用from pymud import PyMudApp了
|
976
|
+
+ 功能调整: 在没有session的时候,也可以执行#exit命令
|
977
|
+
+ 功能调整: 模块加载和重新加载前,会自动调用模块的unload方法(若有)
|
978
|
+
+ 功能新增: 增加log功能(待设计)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
pymud/__init__.py,sha256=Ep6JrWv2wwlLV5nI7F6BOA7Ei6yVgnxvhIRwFGPr2Oc,501
|
2
|
+
pymud/__main__.py,sha256=ybi8pA0BXCZJ_GWgndllT7f1FBBE2tGNCByN0zkaSu0,6748
|
3
|
+
pymud/dialogs.py,sha256=D0ZtCeoBchF5eYzXumkOi3p-maCQZu4v9-wJgxQ790o,6500
|
4
|
+
pymud/extras.py,sha256=QwWwLavVtuXfg0Qb0f_040va1_kej27P-ZB_19HB6Qk,42422
|
5
|
+
pymud/logger.py,sha256=39I_rFBU5MEiOTJyU7GAy9cY_MpYHKiXNgBhpokQrTA,5358
|
6
|
+
pymud/objects.py,sha256=0aUc-PZfVSdYPEAamY0eO6k3P9wJcw6PNxfSE8eZZeE,38072
|
7
|
+
pymud/pkuxkx.py,sha256=vWXHU6GF0HQ0eWb3LmxFVRP0cKnigffCX7Z-LJvwVtw,11496
|
8
|
+
pymud/protocol.py,sha256=QfDXjlg2OcJXmVoXf_3mAemnYotRXDUlEZNQjhkfXdA,49106
|
9
|
+
pymud/pymud.py,sha256=2dbgnzlG9476esOfyp9sv-lRSTpMuRaCXnqrOofOu24,48182
|
10
|
+
pymud/session.py,sha256=uAWt0-AZWpDvVfCjzoxWDpuOwCQ8s1DN_DZ-rVAC5xs,121232
|
11
|
+
pymud/settings.py,sha256=XkyFWTskPGZljuWCaC8cU18ie8-FkecEtb0h3OfcfjU,7145
|
12
|
+
pymud-0.20.0a1.dist-info/LICENSE.txt,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
13
|
+
pymud-0.20.0a1.dist-info/METADATA,sha256=N57G7pGHXWfYKJ2YwJjWFMSYxvfkvwIpmsTKTR1hjro,68201
|
14
|
+
pymud-0.20.0a1.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
|
15
|
+
pymud-0.20.0a1.dist-info/entry_points.txt,sha256=diPUOtTkhgC1hVny7Cdg4aRhaHSynMQoraE7ZhJxUcw,37
|
16
|
+
pymud-0.20.0a1.dist-info/top_level.txt,sha256=8Gp1eXjxixXjqhhti6tLCspV_8s9sNV3z5Em2_KRhD4,6
|
17
|
+
pymud-0.20.0a1.dist-info/RECORD,,
|
pymud-0.19.4.dist-info/RECORD
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
pymud/__init__.py,sha256=5SteBKAKAizt98QOxDRuGxRHhoDAijdG8lC5U7az2SM,422
|
2
|
-
pymud/__main__.py,sha256=I6jFLnTErSuM0G828WUx5EIAmMNAzqYr2AT1zdtGmp0,4636
|
3
|
-
pymud/dialogs.py,sha256=SRNHOashupNi7128Bg_35J1rrI2TsZEakTbaMW5d9PU,5596
|
4
|
-
pymud/extras.py,sha256=p2fgnzKFtaAvcYqayMNSZhrC3BpvfkbF_la3SfGaWBA,42400
|
5
|
-
pymud/objects.py,sha256=hERTIctGF660CoHwZf00-5RZ1-bqCPK2QuwmTZ7NceU,38088
|
6
|
-
pymud/pkuxkx.py,sha256=vWXHU6GF0HQ0eWb3LmxFVRP0cKnigffCX7Z-LJvwVtw,11496
|
7
|
-
pymud/protocol.py,sha256=QfDXjlg2OcJXmVoXf_3mAemnYotRXDUlEZNQjhkfXdA,49106
|
8
|
-
pymud/pymud.py,sha256=HgmAXBXNMjo8xKAVQRS2XOshp-hieEy7bjvaXGq_0Y0,43230
|
9
|
-
pymud/session.py,sha256=18QIArVTPKEBETv4pTWSXssy12XbM4cFj0IhCyio49U,114503
|
10
|
-
pymud/settings.py,sha256=p1YPxDAh0isRsoRVGOgklawXYkzUE490irNiizWylB4,7092
|
11
|
-
pymud-0.19.4.dist-info/LICENSE.txt,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
12
|
-
pymud-0.19.4.dist-info/METADATA,sha256=ApUZTZZRwQvR0vXUAd1gMTlcfLjT3NWYz2mKtmuHJmo,67839
|
13
|
-
pymud-0.19.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
14
|
-
pymud-0.19.4.dist-info/entry_points.txt,sha256=diPUOtTkhgC1hVny7Cdg4aRhaHSynMQoraE7ZhJxUcw,37
|
15
|
-
pymud-0.19.4.dist-info/top_level.txt,sha256=8Gp1eXjxixXjqhhti6tLCspV_8s9sNV3z5Em2_KRhD4,6
|
16
|
-
pymud-0.19.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|