Skip to content

Commit

Permalink
fixed compatibility with mcdr 2.6, supported language preference, `!!…
Browse files Browse the repository at this point in the history
…seen reload`
  • Loading branch information
Ra1ny-Yuki committed Nov 23, 2022
1 parent 0aff79d commit 0d47c42
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 132 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
seen.json
.vscode/settings.json
.idea

*.mcdr
9 changes: 5 additions & 4 deletions lang/en_us.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,18 @@ mcd_seen.text.top_normal: §dplayer only§r
mcd_seen.text.top_bot: §5bot only§r
mcd_seen.text.top_merge: §7all players§r/§emerged§r
mcd_seen.text.top_all: §7all players§r
mcd_seen.text.reloaded: Plugin reloaded

# Hover texts
mcd_seen.hover.help_msg_suggest: "Click to fill {}"
mcd_seen.hover.query_player: "Click to query player {}"
mcd_seen.hover.show_help: "Click to show help"

# Format texts
mcd_seen.fmt.delta_time: "sec min hrs day"
mcd_seen.fmt.seen_top: "Here are the {} players §cofflined§r for the longest time({}):"
mcd_seen.fmt.seen_top_full: "Here are all the players' offline time data({})"
mcd_seen.fmt.liver_top: "Here are the players §acurrently online§r({}):"
mcd_seen.fmt.time_seen: "sec min hrs day"
mcd_seen.fmt.seen_top: "Here are the {num} players §cofflined§r for the longest time({arg}):"
mcd_seen.fmt.seen_top_full: "Here are all the players' offline time data({arg})"
mcd_seen.fmt.liver_top: "Here are the players §acurrently online§r({arg}):"

# Error texts
mcd_seen.error.player_data_not_found: Player data not found! Click here for help
Expand Down
9 changes: 5 additions & 4 deletions lang/zh_cn.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,18 @@ mcd_seen.text.top_normal: §d仅真人玩家§r
mcd_seen.text.top_bot: §5仅假人§r
mcd_seen.text.top_merge: §7所有玩家§r/§e合并显示§r
mcd_seen.text.top_all: §7所有玩家§r
mcd_seen.text.reloaded: 插件已重载

# Hover texts
mcd_seen.hover.help_msg_suggest: "点击以填入§7{}§r"
mcd_seen.hover.query_player: "点击以查询玩家§e{}§r的数据"
mcd_seen.hover.show_help: "点击以获取插件帮助信息"

# Format texts
mcd_seen.fmt.delta_time: "秒 分 小时 天"
mcd_seen.fmt.seen_top: "摸鱼榜前{}的§c鸽子§r({})如下: "
mcd_seen.fmt.seen_top_full: "摸鱼榜全部§c鸽子§r({})如下: "
mcd_seen.fmt.liver_top: "当前在线的§a肝帝§r({})如下: "
mcd_seen.fmt.time_seen: "秒 分 小时 天"
mcd_seen.fmt.seen_top: "摸鱼榜前§6{num}§r的§c鸽子§r({arg})如下: "
mcd_seen.fmt.seen_top_full: "摸鱼榜全部§c鸽子§r({arg})如下: "
mcd_seen.fmt.liver_top: "当前在线的§a肝帝§r({arg})如下: "

# Error texts
mcd_seen.error.player_data_not_found: 没有该玩家的数据
Expand Down
12 changes: 8 additions & 4 deletions mcd_seen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
from mcdreforged.api.types import Info, PluginServerInterface
from mcdreforged.api.decorator import new_thread

from mcd_seen.constants import SEEN_PREFIX, META
from mcd_seen.utils import verify_player_name, bot_name, tr, logger
from mcd_seen.utils import verify_player_name, bot_name, tr, logger, psi
from mcd_seen.storage import storage, bot_list
from mcd_seen.config import config
from mcd_seen.interface import register_command
Expand All @@ -29,13 +28,18 @@ def on_server_stop(*args, **kwargs):
storage.correct([])


@new_thread(META.name + '_PluginLoad')
def on_unload(*args, **kwargs):
logger.unset_file()


@new_thread(psi.get_self_metadata().name + '_PluginLoad')
def warn_first_load():
logger.warning('Load Seen plugin when server is empty is suggested to make sure all the datas are right')


def on_load(server: PluginServerInterface, prev_module):
server.register_help_message(SEEN_PREFIX, tr('mcd_seen.text.reg_help_msg'))
for prefix in config.seen_prefix:
server.register_help_message(prefix, tr('mcd_seen.text.reg_help_msg'))
register_command(server)
if prev_module is not None:
try:
Expand Down
58 changes: 54 additions & 4 deletions mcd_seen/config.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,76 @@
import json

from mcd_seen.constants import CONFIG_FILE

from typing import Union, List
from mcdreforged.api.utils import Serializable
from mcdreforged.api.types import ServerInterface

from mcd_seen.constants import CONFIG_FILE


psi = ServerInterface.get_instance().as_plugin_server_interface()


class Config(Serializable):
primary_prefix: Union[str, List[str]] = '!!seen'
primary_rank_prefix: Union[str, List[str]] = '!!seen-top'
secondary_rank_prefix: Union[str, List[str]] = '!!liver-top'
seen_top_max: int = 10
player_prior_in_merge: bool = True
log_seens: bool = True
identify_bot: bool = True
bot_list_delay: float = 0.3
verbosity: bool
debug_commands: bool
debug_prefixes: Union[str, List[str]]

@staticmethod
def get_iterable(original: Union[str, List[str]]) -> List[str]:
if isinstance(original, str):
return [original]
return original

@property
def seen_prefix(self):
return self.get_iterable(self.primary_prefix)

@property
def seen_top_prefix(self):
return self.get_iterable(self.primary_rank_prefix)

@property
def liver_top_prefix(self):
return self.get_iterable(self.secondary_rank_prefix)

@property
def debug_prefix(self):
return self.get_iterable(self.serialize().get('debug_prefixes', '!!liver'))

@property
def prefixes(self):
result = []
for item in [self.seen_prefix, self.seen_top_prefix, self.liver_top_prefix]:
result += item
return result

@property
def verbose_mode(self):
return self.serialize().get('verbosity', False)

@property
def debug(self):
return self.serialize().get('debug_commands', False)

def save(self) -> None:
with open(CONFIG_FILE, 'w', encoding='UTF-8') as f:
json.dump(self.serialize(), f, indent=4, ensure_ascii=False)

@classmethod
def load(cls) -> 'Config':
return ServerInterface.get_instance().as_plugin_server_interface().load_config_simple(
CONFIG_FILE, default_config=cls.get_default().serialize(), in_data_folder=False, echo_in_console=True,
return psi.load_config_simple(
CONFIG_FILE,
default_config=cls.get_default().serialize(),
in_data_folder=False,
echo_in_console=True,
target_class=cls
)

Expand Down
17 changes: 3 additions & 14 deletions mcd_seen/constants.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import os
from mcdreforged.api.types import ServerInterface

# Command prefixes
SEEN_PREFIX = '!!seen'
SEEN_TOP_PREFIX = '!!seen-top'
LIVER_TOP_PREFIX = '!!liver-top'
DEBUG_PREFIX = '!!liver'


def ensure(folder: str):
Expand All @@ -18,11 +11,7 @@ def ensure(folder: str):
DATA_FOLDER = ensure('config/seen')
CONFIG_FILE = os.path.join(DATA_FOLDER, 'config.json')
SEENS_FILE = os.path.join(DATA_FOLDER, 'seen.json')
LOG_FILE = os.path.join(DATA_FOLDER, 'player_seens.log')
LOG_FILE = os.path.join(DATA_FOLDER, 'logs', 'seen.log')
SEENS_PATH_OLD = ['seen.json', 'config/seen.json']

# Plugin Metadata
META = ServerInterface.get_instance().get_plugin_metadata('mcd_seen')

# Debug mode
DEBUG_MODE = False
OLD_LOG_FILE = os.path.join(DATA_FOLDER, 'player_seens.log')
NEW_LOG_PATH = os.path.join(DATA_FOLDER, 'logs', 'old_seens.log')
111 changes: 59 additions & 52 deletions mcd_seen/interface.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import re
from typing import Callable, Any, List, Union, Optional

from mcdreforged.api.types import CommandSource, PluginServerInterface
from mcdreforged.api.command import *
from mcdreforged.api.utils import Serializable
from mcdreforged.api.rtext import *
from typing import Callable, Any, List, Union, Optional
from mcdreforged.api.types import CommandSource, PluginServerInterface
from mcdreforged.api.utils import Serializable

from mcd_seen.storage import storage, PlayerSeen
from mcd_seen.constants import *
from mcd_seen.config import config
from mcd_seen.utils import tr, delta_time, formatted_time, bot_name
from mcd_seen.storage import storage, PlayerSeen
from mcd_seen.utils import tr, delta_time, bot_name, psi, ntr, htr, fmt_time_tr

TOP_OPTIONS = {
'-bot': 'bot',
Expand All @@ -36,7 +34,7 @@ def parse(cls, arg_string: Optional[str], liver: bool = False):
data[mode] = True
args.remove(arg)
data = cls.deserialize(data)
# Confict check
# Conflict check
if [data.all, data.merge, data.bot].count(True) > 1 or (data.full and liver) or (len(args) != 0):
raise IllegalArgument(f'Illegal argument: {arg_string}', 1)
if liver and not data.get_all:
Expand All @@ -55,7 +53,12 @@ def text(self):
ret.append(tr(f'text.top_{o}'))
if len(ret) == 0:
ret.append(tr('text.top_normal'))
return '/'.join(ret).strip('/')
return RTextBase.join('/', ret)


def reload_self(source: CommandSource):
psi.reload_plugin(psi.get_self_metadata().id)
source.reply(tr('text.reloaded'))


def register_command(server: PluginServerInterface):
Expand All @@ -66,25 +69,31 @@ def exe(func: Union[Callable[[CommandSource, str], Any], Callable[[CommandSource

# !!seen
server.register_command(
Literal(SEEN_PREFIX).on_child_error(CommandError, cmd_error, handled=True).runs(show_help).then(
Literal(config.seen_prefix).on_child_error(
CommandError, cmd_error, handled=True).runs(show_help).then(
Literal('reload').runs(reload_self)
).then(
QuotableText('player').runs(exe(seen))
)
)
# !!seen-top
server.register_command(
Literal(SEEN_TOP_PREFIX).on_child_error(CommandError, cmd_error, handled=True).runs(exe(seen_top, True)).then(
Literal(config.seen_top_prefix).on_child_error(
CommandError, cmd_error, handled=True).runs(exe(seen_top, True)).then(
GreedyText('exarg').runs(exe(seen_top))
)
)
# !!liver
server.register_command(
Literal(LIVER_TOP_PREFIX).on_child_error(CommandError, cmd_error, handled=True).runs(exe(liver_top, True)).then(
Literal(config.liver_top_prefix).on_child_error(
CommandError, cmd_error, handled=True).runs(exe(liver_top, True)).then(
QuotableText('exarg').runs(exe(liver_top))
)
)
if DEBUG_MODE:
if config.debug:
server.register_command(
Literal(DEBUG_PREFIX).requires(lambda src: src.has_permission(4), lambda: 'Permission denied').then(
Literal(config.debug_prefix).requires(
lambda src: src.has_permission(4), lambda: 'Permission denied').then(
Literal('remove').then(
GreedyText('players').runs(exe(__remove_player_data))
)
Expand All @@ -93,49 +102,51 @@ def exe(func: Union[Callable[[CommandSource, str], Any], Callable[[CommandSource


def show_help(source: CommandSource):
help_message = tr(
'help_msg', SEEN_PREFIX, SEEN_TOP_PREFIX, LIVER_TOP_PREFIX, META.name, str(META.version)
).strip().splitlines()
help_msg_rtext = ''
for line in help_message:
if help_msg_rtext != '':
help_msg_rtext += '\n'
for PREFIX in [SEEN_PREFIX, SEEN_TOP_PREFIX, LIVER_TOP_PREFIX]:
result = re.search(r'(?<=§7){}[\S ]*?(?=§)'.format(PREFIX), line)
if result is not None:
break
if result is not None:
cmd = result.group().strip() + ' '
help_msg_rtext += RText(line).c(RAction.suggest_command, cmd).h(
tr("hover.help_msg_suggest", cmd.strip()))
else:
help_msg_rtext += line
source.reply(help_msg_rtext)
meta = psi.get_self_metadata()
msg = htr(
'help_msg',
config.seen_prefix[0],
config.seen_top_prefix[0],
config.liver_top_prefix[0],
meta.name,
str(meta.version)
)
source.reply(msg)


# Text layout
def top(top_players: List[PlayerSeen], prefix: Union[RTextBase, str]):
ret, num = RTextList(prefix), 1
ret, num = [prefix], 1
for p in top_players:
ret.append(f'\n{num}. ', seen_format(p))
ret.append(RTextList(f'{num}. ', seen_format(p)))
num += 1
return ret
return RTextBase.join('\n', ret)


def seen_format(player: PlayerSeen):
ret = ''
return tr('text', player=player).set_translator(seen_fmt_tr)


def seen_fmt_tr(translation_key: str, player: PlayerSeen, language: Optional[str] = None, allow_failure: bool = True):
def ttr(key: str, *args, **kwargs):
return ntr(f'{translation_key}.{key}', *args, language=language, allow_failure=allow_failure, **kwargs)
ret = []
# Bot/Player
ret += {}{}§e'.format('5' if player.is_bot else 'd',
tr(f'text.{"bot" if player.is_bot else "player"}').capitalize())
color = '§5' if player.is_bot else '§d'
ret.append(f"{color}{ttr('bot' if player.is_bot else 'player').capitalize()}§r")
# <player_name>
ret += f' §e{player.actual_name}§r '
ret.append(f'§e{player.actual_name}§r')
# has been online/offline for
ret += tr(f'text.{"bot_liver" if player.is_bot else "player_liver"}') if player.online else tr('text.seen')
ret.append(ttr('bot_liver' if player.is_bot else 'player_liver') if player.online else ttr('seen'))
# sec min hrs day
ret += formatted_time(delta_time(player.target))
return RText(ret).h(tr('hover.query_player', player.actual_name)).c(
RAction.run_command, '{} {}'.format(SEEN_PREFIX, player.actual_name)
ret.append(fmt_time_tr(
'mcd_seen.fmt.time_seen', t=delta_time(player.target), language=language, allow_failure=allow_failure))

ret = ' '.join(ret)
ret = RText(ret).h(tr('hover.query_player', player.actual_name)).c(
RAction.run_command, '{} {}'.format(config.seen_prefix[0], player.actual_name)
)
return ret


def seen(source: CommandSource, player: str):
Expand Down Expand Up @@ -163,7 +174,7 @@ def seen_top(source: CommandSource, exarg: str = None, liver: bool = False):
# -full
sorted_list = sorted_list if args.full else sorted_list[:config.seen_top_max]
# get prefix
prefix = tr(f'fmt.seen_top{"_full" if args.full else ""}', config.seen_top_max, args.text)
prefix = tr(f'fmt.seen_top{"_full" if args.full else ""}', num=config.seen_top_max, arg=args.text)
if liver:
prefix = tr('fmt.liver_top', args.text)

Expand All @@ -176,10 +187,8 @@ def liver_top(source: CommandSource, exarg: str = None):

def cmd_error(source: CommandSource):
source.reply(
RText(
tr('mcd_seen.error.cmd_error'), color=RColor.red
).c(
RAction.run_command, SEEN_PREFIX
tr('mcd_seen.error.cmd_error').set_color(color=RColor.red).c(
RAction.run_command, config.seen_prefix[0]
).h(
tr('mcd_seen.hover.show_help')
)
Expand All @@ -188,10 +197,8 @@ def cmd_error(source: CommandSource):

def player_data_not_found(source: CommandSource):
source.reply(
RText(
tr('mcd_seen.error.player_data_not_found'), color=RColor.red
).c(
RAction.run_command, SEEN_PREFIX
tr('mcd_seen.error.player_data_not_found').set_color(color=RColor.red).c(
RAction.run_command, config.seen_prefix[0]
).h(
tr('mcd_seen.hover.show_help')
)
Expand Down
Loading

0 comments on commit 0d47c42

Please sign in to comment.