pymud 0.19.4__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 +46 -15
- pymud/pkuxkx.py +4 -7
- pymud/pymud.py +227 -71
- pymud/session.py +510 -137
- pymud/settings.py +4 -3
- {pymud-0.19.4.dist-info → pymud-0.20.0.dist-info}/METADATA +225 -188
- pymud-0.20.0.dist-info/RECORD +19 -0
- {pymud-0.19.4.dist-info → pymud-0.20.0.dist-info}/WHEEL +1 -1
- pymud-0.19.4.dist-info/RECORD +0 -16
- {pymud-0.19.4.dist-info → pymud-0.20.0.dist-info}/LICENSE.txt +0 -0
- {pymud-0.19.4.dist-info → pymud-0.20.0.dist-info}/entry_points.txt +0 -0
- {pymud-0.19.4.dist-info → pymud-0.20.0.dist-info}/top_level.txt +0 -0
pymud/modules.py
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
|
2
|
+
import importlib, importlib.util
|
3
|
+
from abc import ABC, ABCMeta
|
4
|
+
from typing import Any
|
5
|
+
from .objects import BaseObject, Command
|
6
|
+
|
7
|
+
class ModuleInfo:
|
8
|
+
"""
|
9
|
+
模块管理类。对加载的模块文件进行管理。该类型由Session类进行管理,无需人工创建和干预。
|
10
|
+
|
11
|
+
有关模块的分类和使用的详细信息,请参见 `脚本 <scripts.html>`_
|
12
|
+
|
13
|
+
:param module_name: 模块的名称, 应与 import xxx 语法中的 xxx 保持一致
|
14
|
+
:param session: 加载/创建本模块的会话
|
15
|
+
|
16
|
+
"""
|
17
|
+
def __init__(self, module_name: str, session):
|
18
|
+
self.session = session
|
19
|
+
self._name = module_name
|
20
|
+
self._ismainmodule = False
|
21
|
+
self.load()
|
22
|
+
|
23
|
+
def _load(self, reload = False):
|
24
|
+
result = True
|
25
|
+
if reload:
|
26
|
+
self._module = importlib.reload(self._module)
|
27
|
+
else:
|
28
|
+
self._module = importlib.import_module(self.name)
|
29
|
+
self._config = {}
|
30
|
+
for attr_name in dir(self._module):
|
31
|
+
attr = getattr(self._module, attr_name)
|
32
|
+
if isinstance(attr, type) and attr.__module__ == self._module.__name__:
|
33
|
+
if (attr_name == "Configuration") or issubclass(attr, IConfig):
|
34
|
+
try:
|
35
|
+
self._config[f"{self.name}.{attr_name}"] = attr(self.session, reload = reload)
|
36
|
+
self.session.info(f"配置对象 {self.name}.{attr_name} {'重新' if reload else ''}创建成功.")
|
37
|
+
except Exception as e:
|
38
|
+
result = False
|
39
|
+
self.session.error(f"配置对象 {self.name}.{attr_name} 创建失败. 错误信息为: {e}")
|
40
|
+
self._ismainmodule = (self._config != {})
|
41
|
+
return result
|
42
|
+
|
43
|
+
def _unload(self):
|
44
|
+
for key, config in self._config.items():
|
45
|
+
if isinstance(config, Command):
|
46
|
+
# Command 对象在从会话中移除时,自动调用其 unload 系列方法,因此不能产生递归
|
47
|
+
self.session.delObject(config)
|
48
|
+
|
49
|
+
else:
|
50
|
+
|
51
|
+
if hasattr(config, "__unload__"):
|
52
|
+
unload = getattr(config, "__unload__", None)
|
53
|
+
if callable(unload): unload()
|
54
|
+
|
55
|
+
if hasattr(config, "unload"):
|
56
|
+
unload = getattr(config, "unload", None)
|
57
|
+
if callable(unload): unload()
|
58
|
+
|
59
|
+
if isinstance(config, BaseObject):
|
60
|
+
self.session.delObject(config)
|
61
|
+
|
62
|
+
del config
|
63
|
+
self._config.clear()
|
64
|
+
|
65
|
+
def load(self):
|
66
|
+
"加载模块内容"
|
67
|
+
if self._load():
|
68
|
+
self.session.info(f"{'主' if self.ismainmodule else '从'}配置模块 {self.name} 加载完成.")
|
69
|
+
else:
|
70
|
+
self.session.error(f"{'主' if self.ismainmodule else '从'}配置模块 {self.name} 加载失败.")
|
71
|
+
|
72
|
+
def unload(self):
|
73
|
+
"卸载模块内容"
|
74
|
+
self._unload()
|
75
|
+
self._loaded = False
|
76
|
+
self.session.info(f"{'主' if self.ismainmodule else '从'}配置模块 {self.name} 卸载完成.")
|
77
|
+
|
78
|
+
def reload(self):
|
79
|
+
"模块文件更新后调用,重新加载已加载的模块内容"
|
80
|
+
self._unload()
|
81
|
+
self._load(reload = True)
|
82
|
+
self.session.info(f"{'主' if self.ismainmodule else '从'}配置模块 {self.name} 重新加载完成.")
|
83
|
+
|
84
|
+
@property
|
85
|
+
def name(self):
|
86
|
+
"只读属性,模块名称"
|
87
|
+
return self._name
|
88
|
+
|
89
|
+
@property
|
90
|
+
def module(self):
|
91
|
+
"只读属性,模块文件的 ModuleType 对象"
|
92
|
+
return self._module
|
93
|
+
|
94
|
+
@property
|
95
|
+
def config(self):
|
96
|
+
"只读字典属性,根据模块文件 ModuleType 对象创建的其中名为 Configuration 的类型或继承自 IConfig 的子类型实例(若有)"
|
97
|
+
return self._config
|
98
|
+
|
99
|
+
@property
|
100
|
+
def ismainmodule(self):
|
101
|
+
"只读属性,区分是否主模块(即包含具体config的模块)"
|
102
|
+
return self._ismainmodule
|
103
|
+
|
104
|
+
class IConfig(metaclass = ABCMeta):
|
105
|
+
"""
|
106
|
+
用于提示PyMUD应用是否自动创建该配置类型的基础类(模拟接口)。
|
107
|
+
|
108
|
+
继承 IConfig 类型让应用自动管理该类型,唯一需要的是,构造函数中,仅存在一个必须指定的参数 Session。
|
109
|
+
|
110
|
+
在应用自动创建 IConfig 实例时,除 session 参数外,还会传递一个 reload 参数 (bool类型),表示是首次加载还是重新加载特性。
|
111
|
+
可以从kwargs 中获取该参数,并针对性的设计相应代码。例如,重新加载相关联的其他模块等。
|
112
|
+
"""
|
113
|
+
def __init__(self, session, *args, **kwargs):
|
114
|
+
self.session = session
|
115
|
+
|
116
|
+
def __unload__(self):
|
117
|
+
if self.session:
|
118
|
+
self.session.delObject(self)
|
119
|
+
|
120
|
+
class Plugin:
|
121
|
+
"""
|
122
|
+
插件管理类。对加载的插件文件进行管理。该类型由PyMudApp进行管理,无需人工创建。
|
123
|
+
|
124
|
+
有关插件的详细信息,请参见 `插件 <plugins.html>`_
|
125
|
+
|
126
|
+
:param name: 插件的文件名, 如'myplugin.py'
|
127
|
+
:param location: 插件所在的目录。自动加载的插件包括PyMUD包目录下的plugins目录以及当前目录下的plugins目录
|
128
|
+
|
129
|
+
"""
|
130
|
+
def __init__(self, name, location):
|
131
|
+
self._plugin_file = name
|
132
|
+
self._plugin_loc = location
|
133
|
+
|
134
|
+
self.reload()
|
135
|
+
|
136
|
+
def reload(self):
|
137
|
+
"加载/重新加载插件对象"
|
138
|
+
#del self.modspec, self.mod
|
139
|
+
self.modspec = importlib.util.spec_from_file_location(self._plugin_file[:-3], self._plugin_loc)
|
140
|
+
self.mod = importlib.util.module_from_spec(self.modspec)
|
141
|
+
self.modspec.loader.exec_module(self.mod)
|
142
|
+
|
143
|
+
self._app_init = self.mod.__dict__["PLUGIN_PYMUD_START"]
|
144
|
+
self._session_create = self.mod.__dict__["PLUGIN_SESSION_CREATE"]
|
145
|
+
self._session_destroy = self.mod.__dict__["PLUGIN_SESSION_DESTROY"]
|
146
|
+
|
147
|
+
@property
|
148
|
+
def name(self):
|
149
|
+
"插件名称,由插件文件中的 PLUGIN_NAME 常量定义"
|
150
|
+
return self.mod.__dict__["PLUGIN_NAME"]
|
151
|
+
|
152
|
+
@property
|
153
|
+
def desc(self):
|
154
|
+
"插件描述,由插件文件中的 PLUGIN_DESC 常量定义"
|
155
|
+
return self.mod.__dict__["PLUGIN_DESC"]
|
156
|
+
|
157
|
+
@property
|
158
|
+
def help(self):
|
159
|
+
"插件帮助,由插件文件中的文档字符串定义"
|
160
|
+
return self.mod.__doc__
|
161
|
+
|
162
|
+
def onAppInit(self, app):
|
163
|
+
"""
|
164
|
+
PyMUD应用启动时对插件执行的操作,由插件文件中的 PLUGIN_PYMUD_START 函数定义
|
165
|
+
|
166
|
+
:param app: 启动的 PyMudApp 对象实例
|
167
|
+
"""
|
168
|
+
self._app_init(app)
|
169
|
+
|
170
|
+
def onSessionCreate(self, session):
|
171
|
+
"""
|
172
|
+
新会话创建时对插件执行的操作,由插件文件中的 PLUGIN_SESSION_CREATE 函数定义
|
173
|
+
|
174
|
+
:param session: 新创建的会话对象实例
|
175
|
+
"""
|
176
|
+
self._session_create(session)
|
177
|
+
|
178
|
+
def onSessionDestroy(self, session):
|
179
|
+
"""
|
180
|
+
会话关闭时(注意不是断开)对插件执行的操作,由插件文件中的 PLUGIN_SESSION_DESTROY 函数定义
|
181
|
+
|
182
|
+
:param session: 所关闭的会话对象实例
|
183
|
+
"""
|
184
|
+
self._session_destroy(session)
|
185
|
+
|
186
|
+
def __getattr__(self, __name: str) -> Any:
|
187
|
+
if hasattr(self.mod, __name):
|
188
|
+
return self.mod.__getattribute__(__name)
|
pymud/objects.py
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
MUD会话(session)中, 支持的对象列表
|
3
3
|
"""
|
4
4
|
|
5
|
-
import asyncio, logging, re
|
5
|
+
import asyncio, logging, re, importlib
|
6
|
+
from abc import ABC, ABCMeta, abstractmethod
|
6
7
|
from collections.abc import Iterable
|
7
8
|
from collections import namedtuple
|
8
|
-
import functools
|
9
9
|
from typing import Any
|
10
10
|
from .settings import Settings
|
11
11
|
|
@@ -148,7 +148,7 @@ class CodeLine:
|
|
148
148
|
return new_code_str, new_code
|
149
149
|
|
150
150
|
async def async_execute(self, session, *args, **kwargs):
|
151
|
-
await session.exec_code_async(self, *args, **kwargs)
|
151
|
+
return await session.exec_code_async(self, *args, **kwargs)
|
152
152
|
|
153
153
|
class CodeBlock:
|
154
154
|
"""
|
@@ -278,14 +278,16 @@ class CodeBlock:
|
|
278
278
|
"""
|
279
279
|
以异步方式执行该 CodeBlock。参数与 execute 相同。
|
280
280
|
"""
|
281
|
+
result = None
|
281
282
|
for code in self.codes:
|
282
283
|
if isinstance(code, CodeLine):
|
283
|
-
await code.async_execute(session, *args, **kwargs)
|
284
|
+
result = await code.async_execute(session, *args, **kwargs)
|
284
285
|
|
285
286
|
if Settings.client["interval"] > 0:
|
286
287
|
await asyncio.sleep(Settings.client["interval"] / 1000.0)
|
287
288
|
|
288
289
|
session.clean_finished_tasks()
|
290
|
+
return result
|
289
291
|
|
290
292
|
class BaseObject:
|
291
293
|
"""
|
@@ -320,7 +322,12 @@ class BaseObject:
|
|
320
322
|
"内部缩写代码前缀"
|
321
323
|
|
322
324
|
def __init__(self, session, *args, **kwargs):
|
323
|
-
|
325
|
+
from .session import Session
|
326
|
+
if isinstance(session, Session):
|
327
|
+
self.session = session
|
328
|
+
else:
|
329
|
+
assert("session must be an instance of class Session!")
|
330
|
+
|
324
331
|
self._enabled = True # give a default value
|
325
332
|
self.log = logging.getLogger(f"pymud.{self.__class__.__name__}")
|
326
333
|
self.id = kwargs.get("id", session.getUniqueID(self.__class__.__abbr__))
|
@@ -341,6 +348,8 @@ class BaseObject:
|
|
341
348
|
|
342
349
|
self.log.debug(f"对象实例 {self} 创建成功.")
|
343
350
|
|
351
|
+
self.session.addObject(self)
|
352
|
+
|
344
353
|
@property
|
345
354
|
def enabled(self):
|
346
355
|
"可读写属性,使能或取消使能本对象"
|
@@ -391,7 +400,8 @@ class BaseObject:
|
|
391
400
|
return self.__detailed__()
|
392
401
|
|
393
402
|
def __detailed__(self) -> str:
|
394
|
-
|
403
|
+
group = f'group = "{self.group}" ' if self.group else ''
|
404
|
+
return f'<{self.__class__.__name__}> id = "{self.id}" {group}enabled = {self.enabled}'
|
395
405
|
|
396
406
|
class GMCPTrigger(BaseObject):
|
397
407
|
"""
|
@@ -441,7 +451,8 @@ class GMCPTrigger(BaseObject):
|
|
441
451
|
self._onSuccess(self.id, value, value_exp)
|
442
452
|
|
443
453
|
def __detailed__(self) -> str:
|
444
|
-
|
454
|
+
group = f'group = "{self.group}" ' if self.group else ''
|
455
|
+
return f'<{self.__class__.__name__}> name = "{self.id}" value = "{self.value}" {group}enabled = {self.enabled} '
|
445
456
|
|
446
457
|
class MatchObject(BaseObject):
|
447
458
|
"""
|
@@ -476,7 +487,7 @@ class MatchObject(BaseObject):
|
|
476
487
|
super().__init__(session, patterns = patterns, *args, **kwargs)
|
477
488
|
|
478
489
|
def __del__(self):
|
479
|
-
|
490
|
+
pass
|
480
491
|
|
481
492
|
@property
|
482
493
|
def patterns(self):
|
@@ -515,11 +526,11 @@ class MatchObject(BaseObject):
|
|
515
526
|
self._mline = 0
|
516
527
|
|
517
528
|
def reset(self):
|
518
|
-
"复位事件,用于async
|
529
|
+
"复位事件,用于async执行未等待结果时,对事件的复位。仅异步有效。"
|
519
530
|
self.event.clear()
|
520
531
|
|
521
532
|
def set(self):
|
522
|
-
"
|
533
|
+
"设置事件标记,用于人工强制触发,仅异步有效。"
|
523
534
|
self.event.set()
|
524
535
|
|
525
536
|
def match(self, line: str, docallback = True) -> BaseObject.State:
|
@@ -615,7 +626,8 @@ class MatchObject(BaseObject):
|
|
615
626
|
return self.state
|
616
627
|
|
617
628
|
def __detailed__(self) -> str:
|
618
|
-
|
629
|
+
group = f'group = "{self.group}" ' if self.group else ''
|
630
|
+
return f'<{self.__class__.__name__}> id = "{self.id}" {group}enabled = {self.enabled} patterns = "{self.patterns}"'
|
619
631
|
|
620
632
|
class Alias(MatchObject):
|
621
633
|
"""
|
@@ -645,7 +657,8 @@ class SimpleAlias(Alias):
|
|
645
657
|
self._codeblock.execute(self.session, id = id, line = line, wildcards = wildcards)
|
646
658
|
|
647
659
|
def __detailed__(self) -> str:
|
648
|
-
|
660
|
+
group = f'group = "{self.group}" ' if self.group else ''
|
661
|
+
return f'<{self.__class__.__name__}> id = "{self.id}" {group}enabled = {self.enabled} patterns = "{self.patterns}" code = "{self._code}"'
|
649
662
|
|
650
663
|
def __repr__(self) -> str:
|
651
664
|
return self.__detailed__()
|
@@ -696,7 +709,8 @@ class SimpleTrigger(Trigger):
|
|
696
709
|
self._codeblock.execute(self.session, id = id, line = line, raw = raw, wildcards = wildcards)
|
697
710
|
|
698
711
|
def __detailed__(self) -> str:
|
699
|
-
|
712
|
+
group = f'group = "{self.group}" ' if self.group else ''
|
713
|
+
return f'<{self.__class__.__name__}> id = "{self.id}" {group}enabled = {self.enabled} patterns = "{self.patterns}" code = "{self._code}"'
|
700
714
|
|
701
715
|
def __repr__(self) -> str:
|
702
716
|
return self.__detailed__()
|
@@ -717,6 +731,20 @@ class Command(MatchObject):
|
|
717
731
|
super().__init__(session, patterns, sync = False, *args, **kwargs)
|
718
732
|
self._tasks = set()
|
719
733
|
|
734
|
+
def __unload__(self):
|
735
|
+
"""
|
736
|
+
当从会话中移除任务时,会自动调用该函数。
|
737
|
+
可以将命令管理的各子类对象在此处清除。
|
738
|
+
该函数需要在子类中覆盖重写。
|
739
|
+
"""
|
740
|
+
pass
|
741
|
+
|
742
|
+
def unload(self):
|
743
|
+
"""
|
744
|
+
与__unload__方法相同,子类仅需覆盖一种方法就可以
|
745
|
+
"""
|
746
|
+
pass
|
747
|
+
|
720
748
|
def create_task(self, coro, *args, name = None):
|
721
749
|
"""
|
722
750
|
创建并管理任务。由 Command 创建的任务,同时也被 Session 所管理。
|
@@ -968,7 +996,8 @@ class Timer(BaseObject):
|
|
968
996
|
self.startTimer()
|
969
997
|
|
970
998
|
def __detailed__(self) -> str:
|
971
|
-
|
999
|
+
group = f'group = "{self.group}" ' if self.group else ''
|
1000
|
+
return f'<{self.__class__.__name__}> id = "{self.id}" {group}enabled = {self.enabled} timeout = {self.timeout}'
|
972
1001
|
|
973
1002
|
def __repr__(self) -> str:
|
974
1003
|
return self.__detailed__()
|
@@ -990,4 +1019,6 @@ class SimpleTimer(Timer):
|
|
990
1019
|
self._codeblock.execute(self.session, id = id)
|
991
1020
|
|
992
1021
|
def __detailed__(self) -> str:
|
993
|
-
|
1022
|
+
group = f'group = "{self.group}" ' if self.group else ''
|
1023
|
+
return f'<{self.__class__.__name__}> id = "{self.id}" {group}enabled = {self.enabled} timeout = {self.timeout} code = "{self._code}"'
|
1024
|
+
|
pymud/pkuxkx.py
CHANGED
@@ -17,9 +17,9 @@ class Configuration:
|
|
17
17
|
|
18
18
|
# hpbrief long情况下的含义
|
19
19
|
HP_KEYS = (
|
20
|
-
"
|
21
|
-
"
|
22
|
-
"
|
20
|
+
"combat_exp", "potential", "max_neili", "neili", "max_jingli", "jingli",
|
21
|
+
"max_qi", "eff_qi", "qi", "max_jing", "eff_jing", "jing",
|
22
|
+
"vigour/qi", "vigour/yuan", "food", "water", "fighting", "busy"
|
23
23
|
)
|
24
24
|
|
25
25
|
# 类的构造函数,传递参数session,是会话本身
|
@@ -54,7 +54,7 @@ class Configuration:
|
|
54
54
|
|
55
55
|
# 1. fullme的链接对应的触发器,匹配URL
|
56
56
|
# 当匹配成功后,调用ontri_webpage
|
57
|
-
self._triggers["tri_webpage"] = self.tri_webpage = Trigger(self.session, id = 'tri_webpage', patterns = r'^http://fullme.pkuxkx.net/robot.php.+$', group = "sys", onSuccess =
|
57
|
+
self._triggers["tri_webpage"] = self.tri_webpage = Trigger(self.session, id = 'tri_webpage', patterns = r'^http://fullme.pkuxkx.net/robot.php.+$', group = "sys", onSuccess = lambda id, line, wildcards: webbrowser.open(line))
|
58
58
|
# 2. fullme的链接对应的触发器,因为要进行多行匹配(3行),因此匹配模式pattern为3个正则表达式模式构成的元组(所有列表类型均可识别),无需像MushClient一样要指定multiline标识和linesToMatch数量
|
59
59
|
# 当匹配成功后,调用ontri_hpbrief
|
60
60
|
# 特别说明:此处的hpbrief触发匹配,需要set hpbrief long后才可以支持
|
@@ -130,9 +130,6 @@ class Configuration:
|
|
130
130
|
def onTimer(self, name, *args, **kwargs):
|
131
131
|
self.session.info("每2秒都会打印本信息", "定时器测试")
|
132
132
|
|
133
|
-
def ontri_webpage(self, name, line, wildcards):
|
134
|
-
webbrowser.open(line)
|
135
|
-
|
136
133
|
def ontri_hpbrief(self, name, line, wildcards):
|
137
134
|
self.session.setVariables(self.HP_KEYS, wildcards)
|
138
135
|
|