Handle DMs, reply in DM

This commit is contained in:
Tanner
2026-11-04 18:09:36 +00:00
parent 23ecfa4446
commit 459e288407
4 changed files with 263 additions and 16 deletions
+14 -10
View File
@@ -20,8 +20,18 @@ class Commands:
self.g.chat.set_handler(self.handle_chat)
def _send_reply(self, reply, private=False, sender=None):
if private and not reply.startswith('/'):
if isinstance(sender, str) and re.fullmatch(r'[A-Za-z0-9_]{1,16}', sender):
self.g.chat.send('/msg {} {}'.format(sender, reply))
else:
self.g.chat.send('/r ' + reply)
else:
self.g.chat.send(reply)
def handle_chat(self, message):
source, sender, text = message
source, sender, text = message[:3]
reply_target = message[3] if len(message) > 3 else sender
reply = ''
private = False
for_me = False
@@ -46,7 +56,7 @@ class Commands:
else:
return
if '-> me' in meta:
if source == 'DM' or '-> me' in meta:
private = True
if prefix == bot_num:
@@ -64,10 +74,7 @@ class Commands:
(command == 'farm' and not data.lower().startswith('crop'))):
reply = 'not migrated to Minecraft 26.3 yet'
print('Reply:', reply)
if private:
self.g.chat.send('/r ' + reply)
else:
self.g.chat.send(reply)
self._send_reply(reply, private, reply_target)
return
try:
@@ -642,8 +649,5 @@ class Commands:
if len(reply) >= 256:
reply = 'reply too long, check console'
if private and not reply.startswith('/'):
self.g.chat.send('/r ' + reply)
else:
self.g.chat.send(reply)
self._send_reply(reply, private, reply_target)
+11 -3
View File
@@ -34,7 +34,13 @@ def component_text(value):
if 'text' in fields:
return component_text(fields['text']) + component_text(fields.get('extra', []))
if 'translate' in fields:
return component_text(fields['translate']) + component_text(fields.get('with', []))
translation = component_text(fields['translate'])
arguments = fields.get('with', [])
if translation == 'commands.message.display.incoming' and len(arguments) >= 2:
return '{} whispers to you: {}'.format(
component_text(arguments[0]),
component_text(arguments[1]))
return translation + component_text(arguments)
return ''.join(component_text(v) for v in fields.values())
if isinstance(value, (list, tuple)):
return ''.join(component_text(item) for item in value)
@@ -135,12 +141,14 @@ def main(global_state=None):
@connection.listener(SystemChat)
def on_system_chat(packet):
message = component_text(packet.content)
chat_handled = False
if global_state is not None:
global_state.last_chat = message
chat = global_state.get('chat')
if chat is not None:
chat.handle_system_chat(packet, message)
print('[server] ' + message, flush=True)
chat_handled = chat.handle_system_chat(packet, message)
if not chat_handled:
print('[server] ' + message, flush=True)
lowered = message.lower()
if 'already registered' in lowered:
send_auth_command('login')
+50 -3
View File
@@ -1,4 +1,5 @@
"""Chat adapter for the Minecraft 26.3 play protocol."""
import re
import time
from contextlib import nullcontext
@@ -7,6 +8,20 @@ from minecraft.networking.modern import (
)
def parse_incoming_private_message(message):
"""Return (sender_name, body) for common Minecraft incoming-DM forms."""
patterns = (
r'^([A-Za-z0-9_]{1,16}) whispers(?: to you)?:\s*(.*)$',
r'^([A-Za-z0-9_]{1,16})\s*->\s*me:\s*(.*)$',
r'^From ([A-Za-z0-9_]{1,16}):\s*(.*)$',
)
for pattern in patterns:
match = re.match(pattern, message, re.DOTALL)
if match:
return match.group(1), match.group(2)
return None
class ModernChatManager:
def __init__(self, global_state):
self.g = global_state
@@ -19,16 +34,48 @@ class ModernChatManager:
def handle_player_chat(self, packet):
sender = str(packet.sender)
text = '{}: {}'.format(sender, packet.message)
print('[chat] {}: {}'.format(sender, packet.message), flush=True)
sender_name = self.g.get('player_names', {}).get(sender, sender)
registry = getattr(self.g.connection, 'registry_ids', {})
chat_types = registry.get('minecraft:chat_type', {})
chat_type_id = getattr(packet, 'chat_type_id', None)
chat_type = next((name for name, index in chat_types.items()
if index == chat_type_id), None)
is_dm = (
getattr(packet, 'network_target_name_present', False) or
chat_type in (
'minecraft:msg_command_incoming',
'minecraft:msg_command_outgoing',
'minecraft:team_msg_command_incoming',
'minecraft:team_msg_command_outgoing',
)
)
label = '[DM] ' if is_dm else ''
print('[chat] {}{}: {}'.format(label, sender_name, packet.message),
flush=True)
if self.handler:
with self.g.get('reload_lock') or nullcontext():
self.handler(('CHAT', sender, text))
if is_dm:
text = '{} -> me: {}'.format(sender_name, packet.message)
self.handler(('DM', sender, text, sender_name))
else:
text = '{}: {}'.format(sender_name, packet.message)
self.handler(('CHAT', sender, text))
def handle_system_chat(self, packet, message):
incoming = parse_incoming_private_message(message)
if incoming:
sender_name, body = incoming
print('[chat] [DM] {}: {}'.format(sender_name, body), flush=True)
if self.handler:
text = '{} -> me: {}'.format(sender_name, body)
with self.g.get('reload_lock') or nullcontext():
self.handler(('DM', sender_name, text, sender_name))
return True
if self.handler:
with self.g.get('reload_lock') or nullcontext():
self.handler(('SYSTEM', None, message))
return False
def send(self, message):
if not message: