pumpkinplus/modules/mechanics/server/chat.rs
1//! Chat module - chat formatting and word filtering.
2//!
3//! ## Configuration
4//!
5//! | Field | Default | Description |
6//! |---------------|---------|--------------------------------------------------------------------|
7//! | `enabled` | `false` | Whether this module is active |
8//! | `chat_format` | `""` | Custom chat format. Use `{player}` and `{message}` placeholders |
9//! | `chat_filter` | `[]` | List of blocked words/phrases (case-insensitive) |
10//!
11//! ## Placeholders
12//!
13//! | Placeholder | Available in |
14//! |-------------|-------------------------------------------------|
15//! | `{player}` | `chat_format` |
16//! | `{message}` | `chat_format` |
17
18use crate::config::ConfigManager;
19use crate::mechanics::mechanic::Mechanic;
20use pumpkin_plugin_api::events::{EventData, EventHandler, EventPriority, PlayerChatEvent};
21use pumpkin_plugin_api::{Context, Server};
22use serde::{Deserialize, Serialize};
23
24/// Handles chat formatting and word filtering.
25#[derive(Default)]
26pub struct Chat;
27
28impl Mechanic for Chat {
29 fn enabled(&self) -> bool {
30 ConfigManager::get()
31 .map(|cm| cm.get_config::<ChatConfig>().enabled)
32 .unwrap_or(true)
33 }
34
35 fn events(&self, context: &Context) {
36 context
37 .register_event_handler::<PlayerChatEvent, _>(Chat, EventPriority::Highest, true)
38 .expect("failed to register chat event handler");
39 }
40}
41
42impl EventHandler<PlayerChatEvent> for Chat {
43 fn handle(
44 &self,
45 _server: Server,
46 mut event: EventData<PlayerChatEvent>,
47 ) -> EventData<PlayerChatEvent> {
48 let config: ChatConfig = ConfigManager::get()
49 .map(|cm| cm.get_config())
50 .unwrap_or_default();
51
52 if !config.chat_filter.is_empty() {
53 let lower = event.message.to_lowercase();
54 if config
55 .chat_filter
56 .iter()
57 .any(|word| lower.contains(word.as_str()))
58 {
59 event.cancelled = true;
60 return event;
61 }
62 }
63
64 if !config.chat_format.is_empty() {
65 let name = event.player.get_display_name().get_text();
66 let original = event.message.clone();
67 event.message = config
68 .chat_format
69 .replace("{player}", &name)
70 .replace("{message}", &original);
71 }
72
73 event
74 }
75}
76
77/// Configuration for the chat module.
78#[derive(Debug, Clone, Default, Serialize, Deserialize)]
79pub struct ChatConfig {
80 /// Whether this module is active.
81 pub enabled: bool,
82 /// Custom chat format. Use `{player}` and `{message}` as placeholders. Leave empty to disable.
83 pub chat_format: String,
84 /// List of blocked words/phrases. Messages containing any entry (case-insensitive) are cancelled. Leave empty to disable.
85 pub chat_filter: Vec<String>,
86}