1988 lines
87 KiB
PHP
1988 lines
87 KiB
PHP
<?php
|
||
/**
|
||
* Assistant AJAX Handler – Komplette Überarbeitung
|
||
*
|
||
* Integriert alle Plugins:
|
||
* ✔ LiteBans Manager (Ban / Mute / Kick / Warn)
|
||
* ✔ MC Player History (Spielzeit, Login, Online-Status)
|
||
* ✔ BungeeCord Status (Server-IP, Online-Spieler, Sub-Server)
|
||
* ✔ Multi Rules (Regelwerk, Tab-Suche)
|
||
* ✔ WP Multi Wiki (Wiki-Artikel-Suche)
|
||
* ✔ WP Ingame Shop Pro (Items, Kategorien, Daily Deal)
|
||
* ✔ WP Multi Ticket Pro (Ticket erstellen / nachschlagen)
|
||
* ✔ WP Business Forum (Threads, Kategorien)
|
||
* ✔ MC MultiServer Gallery PRO (Galerie-Links)
|
||
* ✔ FAQ (Custom Post Type) (FAQ-Suche)
|
||
* ✔ Manuelles Q&A (Bot-Setup im Backend)
|
||
*
|
||
* Verbesserungen:
|
||
* → Intent-Scoring : Mehrere Absichten pro Nachricht erkannt
|
||
* → Multi-Antworten : Alle passenden Ergebnisse werden kombiniert
|
||
* → Spieler-Extraktion: Robuste Regex für Minecraft-Namen
|
||
* → Caching : Transients für teure DB-Abfragen
|
||
* → Fallback-Kette : Graceful Degradation wenn Plugin fehlt
|
||
*
|
||
* @version 3.1
|
||
* @author M_Viper
|
||
*/
|
||
|
||
if ( ! defined( 'ABSPATH' ) ) exit;
|
||
|
||
// Doppel-Load verhindern (falls alte und neue Datei gleichzeitig eingebunden)
|
||
if ( defined( 'MM_ASSISTANT_AJAX_LOADED' ) ) return;
|
||
define( 'MM_ASSISTANT_AJAX_LOADED', true );
|
||
|
||
// ============================================================
|
||
// AJAX Hooks
|
||
// ============================================================
|
||
|
||
add_action( 'wp_ajax_mm_assistant_query', 'mm_assistant_query' );
|
||
add_action( 'wp_ajax_nopriv_mm_assistant_query', 'mm_assistant_query' );
|
||
|
||
// ============================================================
|
||
// HAUPTFUNKTION
|
||
// ============================================================
|
||
|
||
function mm_assistant_query() {
|
||
// Sicherheit
|
||
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce(
|
||
sanitize_text_field( wp_unslash( $_POST['nonce'] ) ),
|
||
'mm_bot_nonce'
|
||
) ) {
|
||
wp_send_json_error( 'Sicherheitsfehler.' );
|
||
}
|
||
|
||
$raw = sanitize_text_field( wp_unslash( $_POST['q'] ?? '' ) );
|
||
$q = trim( $raw );
|
||
$q_lc = mb_strtolower( $q );
|
||
|
||
if ( strlen( $q ) < 2 ) {
|
||
wp_send_json_success( [
|
||
'reply' => 'Bitte gib eine Frage ein.',
|
||
'parts' => [],
|
||
] );
|
||
}
|
||
|
||
// "regeln:Kategorie" → direkt zu Rules-Intent, kein Scoring nötig
|
||
if ( preg_match( '/^regeln?:/iu', $q ) ) {
|
||
$bot = get_option( 'mm_bot_data', [] );
|
||
$result = mm_intent_rules( $q, $bot );
|
||
wp_send_json_success( mm_build_response( [ $result ] ) );
|
||
}
|
||
|
||
// "ban:Steve" → direkt zu Ban-Intent mit Spielername
|
||
if ( preg_match( '/^ban:(.+)$/iu', $q, $m ) ) {
|
||
$bot = get_option( 'mm_bot_data', [] );
|
||
$name = trim( $m[1] );
|
||
$result = mm_intent_ban_check( $q, $name, $bot );
|
||
wp_send_json_success( mm_build_response( [ $result ] ) );
|
||
}
|
||
|
||
try {
|
||
$bot = get_option( 'mm_bot_data', [] );
|
||
$parts = [];
|
||
|
||
// ---- 1. Manuelle Q&A (höchste Priorität) --------------------
|
||
if ( ! empty( $bot['qa'] ) ) {
|
||
foreach ( $bot['qa'] as $item ) {
|
||
if ( empty( $item['keys'] ) ) continue;
|
||
$keys = array_map( 'trim', explode( ',', mb_strtolower( $item['keys'] ) ) );
|
||
foreach ( $keys as $k ) {
|
||
if ( $k !== '' && strpos( $q_lc, $k ) !== false ) {
|
||
$parts[] = [
|
||
'source' => 'qa',
|
||
'content' => wp_kses_post( $item['val'] ),
|
||
];
|
||
break 2;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( ! empty( $parts ) ) {
|
||
wp_send_json_success( mm_build_response( $parts ) );
|
||
}
|
||
|
||
// ---- Intent-Scores berechnen --------------------------------
|
||
$intents = mm_score_intents( $q_lc, $q );
|
||
arsort( $intents );
|
||
$active = array_filter( $intents, function( $s ) { return $s > 0; } );
|
||
|
||
$player_name = mm_extract_player_name( $q );
|
||
|
||
foreach ( $active as $intent => $score ) {
|
||
// Ban-Intent dominant → Regeln nicht zusätzlich auslösen
|
||
if ( $intent === 'rules' && isset( $intents['ban_check'] ) && $intents['ban_check'] >= 25 ) {
|
||
continue;
|
||
}
|
||
// Ban-Intent dominant → Shop/Ticket/Forum nicht zusätzlich auslösen
|
||
if ( in_array( $intent, array( 'shop', 'ticket', 'forum' ), true )
|
||
&& isset( $intents['ban_check'] ) && $intents['ban_check'] >= 40 ) {
|
||
continue;
|
||
}
|
||
// Player-History dominant → Server-Status nicht zusätzlich auslösen
|
||
if ( $intent === 'server_status'
|
||
&& isset( $intents['player_history'] ) && $intents['player_history'] >= 15 ) {
|
||
continue;
|
||
}
|
||
$result = mm_handle_intent( $intent, $q, $q_lc, $player_name, $bot );
|
||
if ( $result ) {
|
||
$parts[] = $result;
|
||
}
|
||
}
|
||
|
||
if ( empty( $parts ) ) {
|
||
$parts[] = mm_fallback_response( $bot );
|
||
}
|
||
|
||
wp_send_json_success( mm_build_response( $parts ) );
|
||
|
||
} catch ( \Throwable $e ) {
|
||
wp_send_json_success( [
|
||
'reply' => '⚠️ Interner Fehler: ' . esc_html( $e->getMessage() ),
|
||
'parts' => [],
|
||
] );
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// INTENT SCORING
|
||
// Jeder Treffer erhöht den Score; der Intent mit dem höchsten
|
||
// Score wird zuerst beantwortet. Mehrere Intents möglich.
|
||
// ============================================================
|
||
|
||
function mm_score_intents( $q_lc, $q_raw ) {
|
||
$scores = [
|
||
'server_status' => 0,
|
||
'ban_check' => 0,
|
||
'player_history' => 0,
|
||
'rules' => 0,
|
||
'wiki' => 0,
|
||
'shop' => 0,
|
||
'ticket' => 0,
|
||
'forum' => 0,
|
||
'gallery' => 0,
|
||
'faq' => 0,
|
||
];
|
||
|
||
// Server-Status
|
||
foreach ( ['ip', 'adresse', 'join', 'einloggen', 'verbinden', 'server ip',
|
||
'wie komme', 'online spieler', 'wieviele spieler', 'server status',
|
||
'serveradresse', 'spieler online'] as $kw ) {
|
||
if ( strpos( $q_lc, $kw ) !== false ) $scores['server_status'] += 10;
|
||
}
|
||
|
||
// Ban / Mute / Kick
|
||
foreach ( ['ban', 'gebannt', 'gesperrt', 'mute', 'gemutet', 'stumm',
|
||
'kick', 'gekickt', 'warn', 'verwarnung', 'entban', 'unban',
|
||
'antrag', 'sperre', 'strafe', 'strafen', 'bestraft',
|
||
'meine strafen', 'mein ban', 'sanktion'] as $kw ) {
|
||
if ( strpos( $q_lc, $kw ) !== false ) $scores['ban_check'] += 15;
|
||
}
|
||
// Stärker gewichten bei Ich-Bezug
|
||
if ( preg_match( '/\b(bin ich|wurde ich|meine|mein)\b.*(gebannt|gesperrt|gemutet|strafe|ban|mute)/i', $q_raw ) ) {
|
||
$scores['ban_check'] += 20;
|
||
}
|
||
if ( preg_match( '/\b(meine strafen|strafen prüfen|mein status)\b/i', $q_raw ) ) {
|
||
$scores['ban_check'] += 25;
|
||
}
|
||
|
||
// Player History
|
||
foreach ( ['spielzeit', 'playtime', 'zuletzt online', 'last seen',
|
||
'letzte login', 'wie lang', 'wie lange', 'war online',
|
||
'online status', 'ist online', 'stunden gespielt'] as $kw ) {
|
||
if ( strpos( $q_lc, $kw ) !== false ) $scores['player_history'] += 15;
|
||
}
|
||
|
||
// Regeln
|
||
foreach ( array( 'regel', 'regeln', 'regelwerk', 'darf ich', 'ist erlaubt',
|
||
'verboten', 'richtlinie', 'grief', 'stehlen', 'pvp',
|
||
'chat regel', 'rule', 'verbote',
|
||
'cheaten', 'cheat', 'hacks', 'hacken', 'modifikation',
|
||
'beleidigung', 'schimpfen', 'respekt' ) as $kw ) {
|
||
if ( strpos( $q_lc, $kw ) !== false ) $scores['rules'] += 12;
|
||
}
|
||
|
||
// Wiki
|
||
foreach ( ['wiki', 'dokumentation', 'guide', 'anleitung', 'howto',
|
||
'how to', 'wie funktioniert', 'erkläre', 'erklärt'] as $kw ) {
|
||
if ( strpos( $q_lc, $kw ) !== false ) $scores['wiki'] += 12;
|
||
}
|
||
|
||
// Shop
|
||
foreach ( [
|
||
'shop', 'kaufen', 'item', 'items', 'preis', 'kosten',
|
||
'coins', 'tagesaktion', 'daily deal', 'angebot', 'kategorie',
|
||
'warenkorb', 'bestellen', 'rang kaufen',
|
||
'was kostet', 'wie teuer', 'preis von', 'kosten von', 'preis für', 'wie viel kostet'
|
||
] as $kw ) {
|
||
if ( strpos( $q_lc, $kw ) !== false ) $scores['shop'] += 12;
|
||
}
|
||
|
||
// Ticket / Support
|
||
foreach ( ['ticket', 'support', 'hilfe', 'problem melden',
|
||
'bug melden', 'melde', 'anfrage', 'beschwerde',
|
||
'anliegen', 'kontaktier'] as $kw ) {
|
||
if ( strpos( $q_lc, $kw ) !== false ) $scores['ticket'] += 12;
|
||
}
|
||
|
||
// Forum
|
||
foreach ( ['forum', 'diskussion', 'thread',
|
||
'neuen thread', 'thread erstellen', 'im forum posten', 'im forum schreiben',
|
||
'forum login', 'passwort vergessen', 'forum kategorie',
|
||
'forum nachricht', 'direktnachricht', 'forum profil',
|
||
'forum rang', 'forum level', 'forum mitglieder'] as $kw ) {
|
||
if ( strpos( $q_lc, $kw ) !== false ) $scores['forum'] += 10;
|
||
}
|
||
|
||
// Gallery
|
||
foreach ( [
|
||
'galerie', 'gallery', 'bild', 'bilder', 'screenshot', 'screenshots',
|
||
'foto', 'fotos', 'tagesbild', 'bild des tages',
|
||
'hochladen', 'upload', 'uploaden', 'bild teilen', 'bilder teilen',
|
||
'verifizier', 'verify', '/verify', 'token galerie',
|
||
'meine galerie', 'spieler galerie', 'galerie upload',
|
||
] as $kw ) {
|
||
if ( strpos( $q_lc, $kw ) !== false ) $scores['gallery'] += 10;
|
||
}
|
||
// Upload-Kontext stärker gewichten
|
||
if ( preg_match( '/\b(wie|kann ich|wie kann|anleitung|schritt)\b.*(bild|foto|screenshot|hochlad|upload)/i', $q_lc ) ) {
|
||
$scores['gallery'] += 20;
|
||
}
|
||
|
||
// FAQ
|
||
foreach ( ['faq', 'häufige frage', 'frequently', 'oft gefragt'] as $kw ) {
|
||
if ( strpos( $q_lc, $kw ) !== false ) $scores['faq'] += 10;
|
||
}
|
||
|
||
return $scores;
|
||
}
|
||
|
||
// ============================================================
|
||
// INTENT HANDLER DISPATCHER
|
||
// ============================================================
|
||
|
||
function mm_handle_intent( $intent, $q, $q_lc, $player_name, $bot ) {
|
||
switch ( $intent ) {
|
||
case 'server_status': return mm_intent_server_status( $bot );
|
||
case 'ban_check': return mm_intent_ban_check( $q, $player_name, $bot );
|
||
case 'player_history': return mm_intent_player_history( $q, $player_name );
|
||
case 'rules': return mm_intent_rules( $q, $bot );
|
||
case 'wiki': return mm_intent_wiki( $q );
|
||
case 'shop': return mm_intent_shop( $q_lc, $bot );
|
||
case 'ticket': return mm_intent_ticket( $q, $bot );
|
||
case 'forum': return mm_intent_forum( $q );
|
||
case 'gallery': return mm_intent_gallery( $bot, $q_lc );
|
||
case 'faq': return mm_intent_faq( $q );
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// ============================================================
|
||
// INTENT: SERVER STATUS (BungeeCord Status Plugin)
|
||
// ============================================================
|
||
|
||
function mm_intent_server_status( $bot ) {
|
||
$lines = array();
|
||
|
||
// ── 1. Statische Infos aus Bot-Setup ──────────────────────
|
||
$ip = $bot['server_ip'] ?? '';
|
||
$ver = $bot['server_ver'] ?? '';
|
||
$specs = $bot['server_specs'] ?? '';
|
||
$discord = $bot['link_discord'] ?? '';
|
||
|
||
if ( $ip ) $lines[] = '🌐 <b>Adresse:</b> <code>' . esc_html( $ip ) . '</code>';
|
||
if ( $ver ) $lines[] = '🎮 <b>Version:</b> ' . esc_html( $ver );
|
||
if ( $specs ) $lines[] = '🖥️ <b>Server:</b> ' . esc_html( $specs );
|
||
|
||
// ── 2. Live-Daten via BungeeCord-Status-Plugin ─────────────
|
||
$servers = get_option( 'mcss_servers', array() );
|
||
|
||
if ( ! empty( $servers ) && function_exists( 'mcss_fetch_server_with_ranks' ) ) {
|
||
$srv = $servers[0];
|
||
$cache_key = 'mm_srv_' . md5( ( $srv['host'] ?? '' ) . ( $srv['player_port'] ?? '9191' ) );
|
||
$data = get_transient( $cache_key );
|
||
|
||
// Caching-Zeit erhöht auf 60 Sekunden
|
||
if ( false === $data ) {
|
||
try {
|
||
$data = mcss_fetch_server_with_ranks( $srv );
|
||
} catch ( \Throwable $e ) {
|
||
$data = null;
|
||
}
|
||
set_transient( $cache_key, $data, 60 );
|
||
}
|
||
|
||
if ( ! empty( $data ) && isset( $data['online'] ) ) {
|
||
$is_online = (bool) $data['online'];
|
||
$icon = $is_online ? '🟢' : '🔴';
|
||
$status = $is_online ? '<b>Online</b>' : '<b>Offline</b>';
|
||
|
||
// Spieler-Anzahl (players = Array von Objekten)
|
||
$player_list = isset( $data['players'] ) && is_array( $data['players'] ) ? $data['players'] : array();
|
||
$player_count = count( $player_list );
|
||
$player_text = $is_online ? ' · <b>' . $player_count . '</b> Spieler online' : '';
|
||
|
||
$srv_label = ! empty( $srv['name'] ) ? esc_html( $srv['name'] ) . ': ' : '';
|
||
$lines[] = $icon . ' ' . $srv_label . $status . $player_text;
|
||
|
||
// Version vom Server
|
||
if ( $is_online && ! empty( $data['version'] ) && empty( $ver ) ) {
|
||
$lines[] = '🎮 <b>Version:</b> ' . esc_html( $data['version'] );
|
||
}
|
||
|
||
// MOTD
|
||
if ( $is_online && ! empty( $data['motd'] ) ) {
|
||
$motd = is_array( $data['motd'] )
|
||
? implode( ' ', $data['motd'] )
|
||
: (string) $data['motd'];
|
||
$motd = trim( $motd );
|
||
if ( $motd ) {
|
||
$lines[] = '💬 ' . mm_format_motd( $motd );
|
||
}
|
||
}
|
||
|
||
// Spieler auflisten (bis 8)
|
||
if ( $is_online && ! empty( $player_list ) ) {
|
||
$names = array();
|
||
foreach ( array_slice( $player_list, 0, 8 ) as $p ) {
|
||
if ( is_array( $p ) && ! empty( $p['name'] ) ) {
|
||
$names[] = esc_html( $p['name'] );
|
||
} elseif ( is_string( $p ) ) {
|
||
$names[] = esc_html( $p );
|
||
}
|
||
}
|
||
if ( ! empty( $names ) ) {
|
||
$lines[] = '👥 <b>Spieler:</b> ' . implode( ', ', $names )
|
||
. ( $player_count > 8 ? ' <small>+' . ( $player_count - 8 ) . ' weitere</small>' : '' );
|
||
}
|
||
}
|
||
}
|
||
|
||
// Weitere konfigurierte Server kurz anzeigen
|
||
if ( count( $servers ) > 1 ) {
|
||
$extra = array();
|
||
foreach ( array_slice( $servers, 1 ) as $s ) {
|
||
if ( empty( $s['name'] ) ) continue;
|
||
$ck = 'mm_srv_' . md5( ( $s['host'] ?? '' ) . ( $s['player_port'] ?? '9191' ) );
|
||
$d = get_transient( $ck );
|
||
if ( false === $d && function_exists( 'mcss_fetch_server_with_ranks' ) ) {
|
||
try {
|
||
$d = mcss_fetch_server_with_ranks( $s );
|
||
} catch ( \Throwable $e ) {
|
||
$d = null;
|
||
}
|
||
set_transient( $ck, $d, 60 );
|
||
}
|
||
$on = ! empty( $d['online'] );
|
||
$count = ( $on && ! empty( $d['players'] ) ) ? count( $d['players'] ) : 0;
|
||
$extra[] = ( $on ? '🟢' : '🔴' ) . ' ' . esc_html( $s['name'] )
|
||
. ( $on ? ' (' . $count . ')' : '' );
|
||
}
|
||
if ( ! empty( $extra ) ) {
|
||
$lines[] = '🗺️ <b>Weitere Server:</b> ' . implode( ' · ', $extra );
|
||
}
|
||
}
|
||
}
|
||
|
||
// ── 3. Kein Plugin, keine Daten ───────────────────────────
|
||
if ( empty( $lines ) ) {
|
||
return array(
|
||
'source' => 'server_status',
|
||
'title' => '🖥️ Server-Status',
|
||
'content' => 'Server-Infos wurden noch nicht konfiguriert. Bitte im Bot-Setup eintragen.',
|
||
);
|
||
}
|
||
|
||
// ── 4. Links (nur Discord, kein Wiki-Link mehr) ─────────────
|
||
if ( $discord ) {
|
||
$lines[] = '💬 <a href="' . esc_url( $discord ) . '" target="_blank">Discord</a>';
|
||
}
|
||
|
||
return array(
|
||
'source' => 'server_status',
|
||
'title' => '🖥️ Server-Status',
|
||
'content' => implode( '<br>', $lines ),
|
||
);
|
||
}
|
||
|
||
// ============================================================
|
||
// HILFSFUNKTION: Minecraft §-Farbcodes → HTML
|
||
// Unterstützt §0-§f (Farben) und §l/§o/§n/§m/§r (Format)
|
||
// ============================================================
|
||
|
||
function mm_format_motd( $text ) {
|
||
if ( empty( $text ) ) return '';
|
||
|
||
$colors = array(
|
||
'0' => '#000000',
|
||
'1' => '#0000AA',
|
||
'2' => '#00AA00',
|
||
'3' => '#00AAAA',
|
||
'4' => '#AA0000',
|
||
'5' => '#AA00AA',
|
||
'6' => '#FFAA00',
|
||
'7' => '#AAAAAA',
|
||
'8' => '#555555',
|
||
'9' => '#5555FF',
|
||
'a' => '#55FF55',
|
||
'b' => '#55FFFF',
|
||
'c' => '#FF5555',
|
||
'd' => '#FF55FF',
|
||
'e' => '#FFFF55',
|
||
'f' => '#FFFFFF',
|
||
);
|
||
|
||
// Normalisieren: § und & beide unterstützen
|
||
$section = "\xC2\xA7"; // UTF-8 für §
|
||
$text = str_replace( '&', $section, $text );
|
||
|
||
// Aufteilen nach §X Segmenten
|
||
$parts = preg_split( '/\xC2\xA7([0-9a-fklmnorA-FKLMNOR])/', $text, -1, PREG_SPLIT_DELIM_CAPTURE );
|
||
$output = '';
|
||
$open = 0;
|
||
$bold = false;
|
||
$italic = false;
|
||
|
||
foreach ( $parts as $i => $part ) {
|
||
if ( $i % 2 === 0 ) {
|
||
// Text-Segment
|
||
if ( $part !== '' ) {
|
||
$output .= esc_html( $part );
|
||
}
|
||
} else {
|
||
// Code-Segment
|
||
$code = strtolower( $part );
|
||
|
||
// Offene Spans schließen
|
||
if ( $open > 0 ) {
|
||
$output .= str_repeat( '</span>', $open );
|
||
$open = 0;
|
||
}
|
||
if ( $bold ) { $output .= '</b>'; $bold = false; }
|
||
if ( $italic ) { $output .= '</i>'; $italic = false; }
|
||
|
||
if ( isset( $colors[ $code ] ) ) {
|
||
$output .= '<span style="color:' . $colors[ $code ] . ';">';
|
||
$open++;
|
||
} elseif ( $code === 'l' ) {
|
||
$output .= '<b>';
|
||
$bold = true;
|
||
} elseif ( $code === 'o' ) {
|
||
$output .= '<i>';
|
||
$italic = true;
|
||
} elseif ( $code === 'r' ) {
|
||
// Reset — alles schon geschlossen oben
|
||
}
|
||
// §k (obfuscated), §m (strike), §n (underline) ignorieren wir
|
||
}
|
||
}
|
||
|
||
// Offene Tags schließen
|
||
if ( $open > 0 ) $output .= str_repeat( '</span>', $open );
|
||
if ( $bold ) $output .= '</b>';
|
||
if ( $italic ) $output .= '</i>';
|
||
|
||
return $output;
|
||
}
|
||
|
||
// ============================================================
|
||
// INTENT: BAN / MUTE / KICK / WARN (LiteBans Manager)
|
||
// ============================================================
|
||
|
||
function mm_intent_ban_check( $q, $player_name, $bot ) {
|
||
$dashboard_url = isset( $bot["litebans_dashboard_url"] ) ? $bot["litebans_dashboard_url"] : "";
|
||
|
||
if ( empty( $player_name ) ) {
|
||
$html = '🔍 <b>Welchen Spielernamen soll ich prüfen?</b><br><br>';
|
||
$html .= '<small style="opacity:.7;">Tippe deinen Minecraft-Namen direkt ein:</small><br><br>';
|
||
$html .= '<div style="display:flex;gap:6px;align-items:center;flex-wrap:wrap;">';
|
||
$html .= '<span style="opacity:.6;font-size:12px;">Format:</span>';
|
||
$html .= '<button class="mm-quick-btn" data-q="ban:DeinName" type="button" '
|
||
. 'style="font-family:monospace;letter-spacing:.5px;">ban:DeinName</button>';
|
||
$html .= '</div>';
|
||
$html .= '<br><small style="opacity:.6;">Ersetze <b>DeinName</b> mit deinem echten Minecraft-Namen.<br>';
|
||
$html .= 'Oder schreibe z.B.: <b>ist Steve gebannt</b></small>';
|
||
if ( $dashboard_url ) {
|
||
$html .= '<br><br>→ <a href="' . esc_url( $dashboard_url ) . '" target="_blank">LiteBans Dashboard öffnen</a>';
|
||
}
|
||
return array( "source" => "ban_check", "title" => "🔨 Strafen prüfen", "content" => $html );
|
||
}
|
||
|
||
$info = mm_litebans_query( $player_name );
|
||
|
||
if ( is_null( $info ) ) {
|
||
return array( "source" => "ban_check", "title" => "🔨 Ban-Status",
|
||
"content" => "⚠️ LiteBans ist nicht konfiguriert oder die Datenbank ist nicht erreichbar." );
|
||
}
|
||
|
||
if ( isset( $info["not_found"] ) ) {
|
||
return array( "source" => "ban_check", "title" => "🔨 Ban-Status: " . esc_html( $player_name ),
|
||
"content" => "❓ Spieler <b>" . esc_html( $player_name ) . "</b> wurde in der LiteBans-Datenbank nicht gefunden." );
|
||
}
|
||
|
||
$lines = array();
|
||
|
||
// Avatar + Name
|
||
$lines[] = "<div style='display:flex;align-items:center;gap:10px;margin-bottom:6px;'>"
|
||
. "<img src='https://minotar.net/avatar/" . esc_attr( $info["player"] ) . "/40' "
|
||
. "style='border-radius:4px;width:40px;height:40px;' alt='Avatar'>"
|
||
. "<b style='font-size:15px;'>" . esc_html( $info["player"] ) . "</b>"
|
||
. "</div>";
|
||
|
||
// Aktiver Ban
|
||
if ( $info["active_ban"] ) {
|
||
$b = $info["active_ban"];
|
||
$lines[] = "<div style='background:rgba(239,68,68,.15);border-left:3px solid #ef4444;padding:8px 10px;border-radius:0 6px 6px 0;margin:4px 0;'>";
|
||
$lines[] = "🚫 <b>Aktiver Ban</b>";
|
||
$lines[] = "📋 Grund: " . esc_html( $b["reason"] );
|
||
$lines[] = "👮 Von: " . esc_html( $b["by"] );
|
||
$lines[] = "⏰ Bis: <b>" . esc_html( $b["until"] ) . "</b>";
|
||
$lines[] = "</div>";
|
||
} else {
|
||
$lines[] = "✅ <b>Kein aktiver Ban</b>";
|
||
}
|
||
|
||
// Aktiver Mute
|
||
if ( $info["active_mute"] ) {
|
||
$m = $info["active_mute"];
|
||
$lines[] = "<div style='background:rgba(245,158,11,.15);border-left:3px solid #f59e0b;padding:8px 10px;border-radius:0 6px 6px 0;margin:4px 0;'>";
|
||
$lines[] = "🔇 <b>Aktiver Mute</b>";
|
||
$lines[] = "📋 Grund: " . esc_html( $m["reason"] );
|
||
$lines[] = "⏰ Bis: <b>" . esc_html( $m["until"] ) . "</b>";
|
||
$lines[] = "</div>";
|
||
} else {
|
||
$lines[] = "✅ <b>Kein aktiver Mute</b>";
|
||
}
|
||
|
||
// Kicks & Warns
|
||
$stats = array();
|
||
if ( $info["kicks"] > 0 ) $stats[] = "👢 <b>" . $info["kicks"] . "</b> Kick(s)";
|
||
if ( $info["warnings"] > 0 ) $stats[] = "⚠️ <b>" . $info["warnings"] . "</b> Verwarnung(en)";
|
||
if ( ! empty( $stats ) ) $lines[] = implode( " · ", $stats );
|
||
|
||
// Ban-History
|
||
if ( ! empty( $info["ban_history"] ) ) {
|
||
$lines[] = "<br><b>🕐 Letzte Bestrafungen:</b>";
|
||
foreach ( $info["ban_history"] as $h ) {
|
||
$icon = $h["type"] === "ban" ? "🚫" : ( $h["type"] === "mute" ? "🔇" : ( $h["type"] === "kick" ? "👢" : "⚠️" ) );
|
||
$lines[] = $icon . " " . esc_html( $h["date"] )
|
||
. " – " . esc_html( $h["reason"] )
|
||
. " <small style='opacity:.6;'>(" . esc_html( $h["by"] ) . ")</small>";
|
||
}
|
||
}
|
||
|
||
// Entbannungsanträge
|
||
if ( ! empty( $info["unban_requests"] ) ) {
|
||
$lines[] = "<br><b>📋 Entbannungsanträge:</b>";
|
||
foreach ( $info["unban_requests"] as $r ) {
|
||
$lines[] = $r["status"] . " – " . esc_html( $r["date"] );
|
||
}
|
||
}
|
||
|
||
// Links
|
||
$link_parts = array();
|
||
if ( $dashboard_url ) {
|
||
$link_parts[] = "<a href='" . esc_url( $dashboard_url ) . "' target='_blank'>Dashboard</a>";
|
||
}
|
||
if ( $info["active_ban"] && $dashboard_url ) {
|
||
$unban_url = add_query_arg( array(
|
||
"lb_player" => $info["player"],
|
||
"lb_reason" => $info["active_ban"]["reason"],
|
||
), $dashboard_url );
|
||
$link_parts[] = "<a href='" . esc_url( $unban_url ) . "' target='_blank'><b>Entbannungsantrag stellen →</b></a>";
|
||
}
|
||
if ( ! empty( $link_parts ) ) $lines[] = "<br>" . implode( " · ", $link_parts );
|
||
|
||
$status_icon = $info["active_ban"] ? "🚫" : "✅";
|
||
|
||
return array(
|
||
"source" => "ban_check",
|
||
"title" => $status_icon . " Ban-Status: " . esc_html( $info["player"] ),
|
||
"content" => implode( "<br>", $lines ),
|
||
);
|
||
}
|
||
|
||
function mm_litebans_query( $player_name ) {
|
||
$settings = get_option( "wp_litebans_pro_settings", array() );
|
||
if ( empty( $settings["db_name"] ) || empty( $settings["db_user"] ) ) return null;
|
||
|
||
$prefix = isset( $settings["table_prefix"] ) ? $settings["table_prefix"] : "litebans_";
|
||
$db_host = isset( $settings["db_host"] ) ? $settings["db_host"] : "localhost";
|
||
if ( ! empty( $settings["db_port"] ) ) $db_host .= ":" . intval( $settings["db_port"] );
|
||
|
||
try {
|
||
$lbdb = new wpdb( $settings["db_user"], $settings["db_pass"], $settings["db_name"], $db_host );
|
||
$lbdb->suppress_errors( true );
|
||
$lbdb->prefix = $prefix;
|
||
} catch ( Exception $e ) {
|
||
return null;
|
||
}
|
||
|
||
$name = sanitize_text_field( $player_name );
|
||
|
||
// UUID über history-Tabelle (wie das Plugin selbst)
|
||
$uuid = $lbdb->get_var( $lbdb->prepare(
|
||
"SELECT uuid FROM {$prefix}history WHERE name = %s ORDER BY id DESC LIMIT 1", $name
|
||
) );
|
||
|
||
if ( empty( $uuid ) ) {
|
||
// Fuzzy-Suche
|
||
$row = $lbdb->get_row( $lbdb->prepare(
|
||
"SELECT uuid, name FROM {$prefix}history WHERE name LIKE %s ORDER BY id DESC LIMIT 1",
|
||
"%" . $lbdb->esc_like( $name ) . "%"
|
||
) );
|
||
if ( ! $row ) return array( "not_found" => true );
|
||
$uuid = $row->uuid;
|
||
$name = $row->name;
|
||
}
|
||
|
||
$info = array( "player" => $name, "active_ban" => null, "active_mute" => null,
|
||
"kicks" => 0, "warnings" => 0, "ban_history" => array(), "unban_requests" => array() );
|
||
|
||
// Aktiver Ban (via UUID)
|
||
$ban = $lbdb->get_row( $lbdb->prepare(
|
||
"SELECT reason, banned_by_name, until, active, removed_by_name FROM {$prefix}bans WHERE uuid = %s AND active = 1 ORDER BY time DESC LIMIT 1", $uuid
|
||
) );
|
||
if ( $ban && intval( $ban->active ) === 1 && empty( $ban->removed_by_name ) ) {
|
||
$until = (int) $ban->until;
|
||
$info["active_ban"] = array(
|
||
"reason" => mm_lb_clean( $ban->reason ),
|
||
"by" => $ban->banned_by_name,
|
||
"until" => ( $until <= 0 ) ? "Permanent" : date_i18n( "d.m.Y H:i", $until / 1000 ),
|
||
);
|
||
}
|
||
|
||
// Aktiver Mute (via UUID)
|
||
$mute = $lbdb->get_row( $lbdb->prepare(
|
||
"SELECT reason, banned_by_name, until, active, removed_by_name FROM {$prefix}mutes WHERE uuid = %s AND active = 1 ORDER BY time DESC LIMIT 1", $uuid
|
||
) );
|
||
if ( $mute && intval( $mute->active ) === 1 && empty( $mute->removed_by_name ) ) {
|
||
$until = (int) $mute->until;
|
||
$info["active_mute"] = array(
|
||
"reason" => mm_lb_clean( $mute->reason ),
|
||
"by" => $mute->banned_by_name,
|
||
"until" => ( $until <= 0 ) ? "Permanent" : date_i18n( "d.m.Y H:i", $until / 1000 ),
|
||
);
|
||
}
|
||
|
||
// Kicks & Warnings (UUID)
|
||
$info["kicks"] = (int) $lbdb->get_var( $lbdb->prepare( "SELECT COUNT(*) FROM {$prefix}kicks WHERE uuid = %s", $uuid ) );
|
||
if ( $lbdb->get_var( "SHOW TABLES LIKE '{$prefix}warnings'" ) ) {
|
||
$info["warnings"] = (int) $lbdb->get_var( $lbdb->prepare( "SELECT COUNT(*) FROM {$prefix}warnings WHERE uuid = %s", $uuid ) );
|
||
}
|
||
|
||
// Ban-History (letzte 5 aller Typen)
|
||
$history = array();
|
||
foreach ( array( "bans", "mutes", "kicks" ) as $t ) {
|
||
$rows = $lbdb->get_results( $lbdb->prepare(
|
||
"SELECT reason, banned_by_name, time FROM {$prefix}{$t} WHERE uuid = %s ORDER BY time DESC LIMIT 3", $uuid
|
||
) );
|
||
$type = rtrim( $t, "s" ); // bans→ban, mutes→mute, kicks→kick
|
||
foreach ( (array) $rows as $r ) {
|
||
$history[] = array( "type" => $type, "date" => date_i18n( "d.m.Y", $r->time / 1000 ),
|
||
"reason" => mm_lb_clean( $r->reason ), "by" => $r->banned_by_name, "ts" => $r->time );
|
||
}
|
||
}
|
||
usort( $history, function( $a, $b ) { return $b["ts"] - $a["ts"]; } );
|
||
$info["ban_history"] = array_slice( $history, 0, 5 );
|
||
|
||
// Entbannungsanträge (WP CPT)
|
||
$requests = get_posts( array( "post_type" => "unban_request", "posts_per_page" => 5,
|
||
"post_status" => "any", "meta_query" => array( array( "key" => "_lb_player", "value" => $name, "compare" => "=" ) ) ) );
|
||
foreach ( $requests as $r ) {
|
||
$status = get_post_meta( $r->ID, "_lb_status", true ) ?: "pending";
|
||
$label = $status === "approved" ? "✅ Angenommen" : ( $status === "rejected" ? "❌ Abgelehnt" : "⏳ Wartend" );
|
||
$info["unban_requests"][] = array( "date" => get_the_date( "d.m.Y", $r->ID ), "status" => $label );
|
||
}
|
||
|
||
return $info;
|
||
}
|
||
|
||
function mm_lb_clean( $text ) {
|
||
if ( ! $text ) return "–";
|
||
return preg_replace( "/(?:§|&)[0-9a-fk-orA-FK-OR]/u", "", $text );
|
||
}
|
||
|
||
// ============================================================
|
||
// INTENT: PLAYER HISTORY (MC Player History Plugin)
|
||
// ============================================================
|
||
// INTENT: PLAYER HISTORY (MC Player History Plugin)
|
||
// ============================================================
|
||
|
||
function mm_intent_player_history( $q, $player_name ) {
|
||
$bot = get_option( 'mm_bot_data', [] );
|
||
|
||
// Nur aktiv wenn URL im Backend gesetzt
|
||
if ( empty( $bot['url_player_history'] ) ) return null;
|
||
|
||
$history_url = $bot['url_player_history'];
|
||
|
||
// Kein Spielername → Top-Liste + Hinweis
|
||
if ( empty( $player_name ) ) {
|
||
global $wpdb;
|
||
$table = $wpdb->prefix . 'mc_players';
|
||
if ( $wpdb->get_var( "SHOW TABLES LIKE '$table'" ) !== $table ) {
|
||
return [
|
||
'source' => 'player_history',
|
||
'title' => '👤 Spieler-History',
|
||
'content' => 'Das MC Player History Plugin ist nicht installiert oder die Tabelle fehlt.',
|
||
];
|
||
}
|
||
|
||
// Top 5 nach Spielzeit
|
||
$top = $wpdb->get_results(
|
||
"SELECT username, prefix, playtime_seconds, is_online
|
||
FROM $table WHERE playtime_seconds > 0
|
||
ORDER BY playtime_seconds DESC LIMIT 5"
|
||
);
|
||
|
||
$html = 'Nenne mir einen Minecraft-Namen, z.B.: <b>Spielzeit von Steve</b><br><br>';
|
||
|
||
if ( ! empty( $top ) ) {
|
||
$html .= '🏆 <b>Top-Spieler:</b><br>';
|
||
$i = 1;
|
||
foreach ( $top as $p ) {
|
||
$prefix_html = mm_ph_format_prefix( $p->prefix );
|
||
$time_str = mm_format_playtime( (int) $p->playtime_seconds );
|
||
$online_dot = $p->is_online ? '🟢' : '⚫';
|
||
$html .= $online_dot . ' ' . ( $prefix_html ? $prefix_html . ' ' : '' )
|
||
. '<b>' . esc_html( $p->username ) . '</b>'
|
||
. ' – ' . esc_html( $time_str ) . '<br>';
|
||
$i++;
|
||
}
|
||
$html .= '<br>';
|
||
}
|
||
|
||
$html .= "→ <a href='" . esc_url( $history_url ) . "' target='_blank'><b>Alle Spieler ansehen</b></a>";
|
||
|
||
return [
|
||
'source' => 'player_history',
|
||
'title' => '👤 Spieler-History',
|
||
'content' => $html,
|
||
];
|
||
}
|
||
|
||
// Spieler suchen
|
||
global $wpdb;
|
||
$table = $wpdb->prefix . 'mc_players';
|
||
|
||
if ( $wpdb->get_var( "SHOW TABLES LIKE '$table'" ) !== $table ) {
|
||
return [
|
||
'source' => 'player_history',
|
||
'title' => '👤 Spieler-Info',
|
||
'content' => 'MC Player History Plugin ist nicht aktiv oder Tabelle fehlt.',
|
||
];
|
||
}
|
||
|
||
// Exakter Treffer
|
||
$row = $wpdb->get_row( $wpdb->prepare(
|
||
"SELECT username, prefix, is_online, first_seen, last_seen, playtime_seconds
|
||
FROM $table WHERE username = %s LIMIT 1",
|
||
$player_name
|
||
) );
|
||
|
||
// Fuzzy-Suche als Fallback
|
||
if ( ! $row ) {
|
||
$row = $wpdb->get_row( $wpdb->prepare(
|
||
"SELECT username, prefix, is_online, first_seen, last_seen, playtime_seconds
|
||
FROM $table WHERE username LIKE %s LIMIT 1",
|
||
'%' . $wpdb->esc_like( $player_name ) . '%'
|
||
) );
|
||
}
|
||
|
||
if ( ! $row ) {
|
||
return [
|
||
'source' => 'player_history',
|
||
'title' => '👤 Spieler: ' . esc_html( $player_name ),
|
||
'content' => '❓ Spieler <b>' . esc_html( $player_name ) . '</b> wurde noch nicht auf dem Server gesehen.'
|
||
. '<br><br>→ <a href="' . esc_url( $history_url ) . '" target="_blank">Spieler-Liste öffnen</a>',
|
||
];
|
||
}
|
||
|
||
$online_icon = $row->is_online
|
||
? '🟢 <b>Gerade online</b>'
|
||
: '⚫ Offline';
|
||
|
||
$playtime = mm_format_playtime( (int) $row->playtime_seconds );
|
||
$last_seen = $row->last_seen
|
||
? wp_date( 'd.m.Y \u\m H:i \U\h\r', strtotime( $row->last_seen ) )
|
||
: 'Unbekannt';
|
||
$first_seen = $row->first_seen
|
||
? wp_date( 'd.m.Y', strtotime( $row->first_seen ) )
|
||
: 'Unbekannt';
|
||
|
||
$lines = [
|
||
'📶 Status: ' . $online_icon,
|
||
'⏱️ Spielzeit: <b>' . esc_html( $playtime ) . '</b>',
|
||
'🕐 Zuletzt gesehen: <b>' . esc_html( $last_seen ) . '</b>',
|
||
'📅 Erstmals gesehen: <b>' . esc_html( $first_seen ) . '</b>',
|
||
];
|
||
|
||
// Rang / Prefix (mit MC-Farben)
|
||
if ( ! empty( $row->prefix ) ) {
|
||
$prefix_html = mm_ph_format_prefix( $row->prefix );
|
||
if ( $prefix_html !== '' ) {
|
||
$lines[] = '🏷️ Rang: ' . $prefix_html;
|
||
}
|
||
}
|
||
|
||
// Avatar
|
||
$lines[] = '<img src="https://mc-heads.net/avatar/' . esc_attr( $row->username ) . '/32" '
|
||
. 'style="border-radius:4px;vertical-align:middle;margin-right:4px;" alt="Avatar"> '
|
||
. '<small><a href="' . esc_url( $history_url ) . '" target="_blank">Spieler-Liste öffnen</a></small>';
|
||
|
||
return [
|
||
'source' => 'player_history',
|
||
'title' => '👤 Spieler: ' . esc_html( $row->username ),
|
||
'content' => implode( '<br>', $lines ),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Minecraft-Farbcodes in HTML umwandeln.
|
||
* Nutzt mcph_parse_minecraft_colors() vom Plugin wenn verfügbar,
|
||
* ansonsten eigene Implementierung als Fallback.
|
||
*/
|
||
function mm_ph_format_prefix( $prefix ) {
|
||
if ( empty( $prefix ) ) return '';
|
||
|
||
// Plugin-eigene Funktion bevorzugen
|
||
if ( function_exists( 'mcph_parse_minecraft_colors' ) ) {
|
||
return mcph_parse_minecraft_colors( $prefix );
|
||
}
|
||
|
||
// Fallback: eigene Implementierung
|
||
$map = [
|
||
'&0' => '<span style="color:#000000">',
|
||
'&1' => '<span style="color:#0000AA">',
|
||
'&2' => '<span style="color:#00AA00">',
|
||
'&3' => '<span style="color:#00AAAA">',
|
||
'&4' => '<span style="color:#AA0000">',
|
||
'&5' => '<span style="color:#AA00AA">',
|
||
'&6' => '<span style="color:#FFAA00">',
|
||
'&7' => '<span style="color:#AAAAAA">',
|
||
'&8' => '<span style="color:#555555">',
|
||
'&9' => '<span style="color:#5555FF">',
|
||
'&a' => '<span style="color:#55FF55">',
|
||
'&b' => '<span style="color:#55FFFF">',
|
||
'&c' => '<span style="color:#FF5555">',
|
||
'&d' => '<span style="color:#FF55FF">',
|
||
'&e' => '<span style="color:#FFFF55">',
|
||
'&f' => '<span style="color:#FFFFFF">',
|
||
'&l' => '<span style="font-weight:bold">',
|
||
'&o' => '<span style="font-style:italic">',
|
||
'&n' => '<span style="text-decoration:underline">',
|
||
'&m' => '<span style="text-decoration:line-through">',
|
||
];
|
||
|
||
// Anzahl geöffneter Spans zählen für sauberes Schließen
|
||
$open = 0;
|
||
$text = str_ireplace( array_keys( $map ), array_values( $map ), esc_html( $prefix ) );
|
||
|
||
// &r = alle schließen
|
||
$text = preg_replace_callback( '/&r/i', function() use ( &$open ) {
|
||
$close = str_repeat( '</span>', $open );
|
||
$open = 0;
|
||
return $close;
|
||
}, $text );
|
||
|
||
// Spans mitzählen
|
||
preg_match_all( '/<span/', $text, $opens );
|
||
preg_match_all( '/<\/span>/', $text, $closes );
|
||
$remaining = count( $opens[0] ) - count( $closes[0] );
|
||
if ( $remaining > 0 ) {
|
||
$text .= str_repeat( '</span>', $remaining );
|
||
}
|
||
|
||
return $text;
|
||
}
|
||
|
||
/** Sekunden in lesbare Spielzeit umrechnen */
|
||
function mm_format_playtime( $seconds ) {
|
||
if ( $seconds <= 0 ) return '0 Minuten';
|
||
$h = floor( $seconds / 3600 );
|
||
$m = floor( ( $seconds % 3600 ) / 60 );
|
||
if ( $h > 0 ) return "{$h}h {$m}min";
|
||
return "{$m} Minuten";
|
||
}
|
||
|
||
// ============================================================
|
||
// INTENT: REGELN (Multi Rules Plugin)
|
||
// ============================================================
|
||
|
||
function mm_intent_rules( $q, $bot ) {
|
||
$rules_url = ! empty( $bot['url_rules'] ) ? $bot['url_rules'] : home_url( '/regeln' );
|
||
$options = get_option( 'mrp_settings' );
|
||
$tabs = isset( $options['tabs'] ) && is_array( $options['tabs'] ) ? $options['tabs'] : array();
|
||
|
||
// ── Schritt 2: Kategorie wurde gewählt → "regeln:Minecraft" ──
|
||
if ( preg_match( '/^regeln?:(.+)$/iu', trim( $q ), $m ) ) {
|
||
$chosen = trim( $m[1] );
|
||
return mm_rules_show_tab( $chosen, $tabs, $rules_url );
|
||
}
|
||
|
||
// ── Spezifische Suche: Suchbegriff vorhanden ──────────────
|
||
$q_clean = trim( preg_replace(
|
||
'/\b(regel|regeln|regelwerk|zeig|alle|zeige|liste|gibt es|was sind)\b/iu',
|
||
'', $q
|
||
) );
|
||
$q_clean = trim( preg_replace( '/\s+/', ' ', $q_clean ) );
|
||
|
||
if ( strlen( $q_clean ) >= 3 ) {
|
||
return mm_rules_search( $q_clean, $tabs, $rules_url );
|
||
}
|
||
|
||
// ── Schritt 1: Allgemein → Kategorie-Auswahl ─────────────
|
||
if ( empty( $tabs ) ) {
|
||
return array(
|
||
'source' => 'rules',
|
||
'title' => '📜 Regelwerk',
|
||
'content' => "→ <a href='" . esc_url( $rules_url ) . "' target='_blank'><b>Zum Regelwerk</b></a>",
|
||
);
|
||
}
|
||
|
||
$html = '<small style="opacity:.7;">Wähle eine Kategorie:</small><br><br>';
|
||
$html .= '<div class="mm-bot-quick" style="flex-direction:column;gap:6px;">';
|
||
|
||
foreach ( $tabs as $tab ) {
|
||
$tab_title = $tab['title'] ?? 'Allgemein';
|
||
$rule_count = ! empty( $tab['rules'] ) ? count( $tab['rules'] ) : 0;
|
||
// Trigger: "regeln:Minecraft"
|
||
$query = 'regeln:' . $tab_title;
|
||
$html .= '<button class="mm-quick-btn" data-q="' . esc_attr( $query ) . '" type="button">'
|
||
. '📂 ' . esc_html( $tab_title )
|
||
. ' <small style="opacity:.6;">(' . $rule_count . ')</small>'
|
||
. '</button>';
|
||
}
|
||
|
||
$html .= '</div>';
|
||
$html .= '<br><small>→ <a href="' . esc_url( $rules_url ) . '" target="_blank">Alle Regeln im Browser öffnen</a></small>';
|
||
|
||
return array(
|
||
'source' => 'rules',
|
||
'title' => '📜 Regelwerk',
|
||
'content' => $html,
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Alle Regeln eines bestimmten Tabs anzeigen.
|
||
*/
|
||
function mm_rules_show_tab( $tab_name, $tabs, $rules_url ) {
|
||
$found_tab = null;
|
||
|
||
foreach ( $tabs as $tab ) {
|
||
if ( mb_strtolower( $tab['title'] ?? '' ) === mb_strtolower( $tab_name ) ) {
|
||
$found_tab = $tab;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Fuzzy-Fallback
|
||
if ( ! $found_tab ) {
|
||
foreach ( $tabs as $tab ) {
|
||
if ( strpos( mb_strtolower( $tab['title'] ?? '' ), mb_strtolower( $tab_name ) ) !== false ) {
|
||
$found_tab = $tab;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( ! $found_tab ) {
|
||
return array(
|
||
'source' => 'rules',
|
||
'title' => '📜 Regelwerk',
|
||
'content' => 'Kategorie <b>' . esc_html( $tab_name ) . '</b> nicht gefunden.<br>'
|
||
. "→ <a href='" . esc_url( $rules_url ) . "' target='_blank'>Zum Regelwerk</a>",
|
||
);
|
||
}
|
||
|
||
$rules = isset( $found_tab['rules'] ) && is_array( $found_tab['rules'] ) ? $found_tab['rules'] : array();
|
||
|
||
if ( empty( $rules ) ) {
|
||
return array(
|
||
'source' => 'rules',
|
||
'title' => '📜 ' . esc_html( $found_tab['title'] ),
|
||
'content' => 'In dieser Kategorie sind noch keine Regeln hinterlegt.',
|
||
);
|
||
}
|
||
|
||
$html = '';
|
||
foreach ( $rules as $rule ) {
|
||
$title = $rule['rule_title'] ?? '';
|
||
$content = $rule['rule_content'] ?? '';
|
||
|
||
$content_plain = wp_strip_all_tags( $content );
|
||
if ( mb_strlen( $content_plain ) > 300 ) {
|
||
$rendered = '<p style="margin:4px 0;font-size:13px;opacity:.9;">'
|
||
. esc_html( mb_substr( $content_plain, 0, 300 ) ) . '…</p>';
|
||
} else {
|
||
$rendered = '<div style="font-size:13px;line-height:1.6;margin-top:4px;opacity:.9;">'
|
||
. wp_kses_post( $content )
|
||
. '</div>';
|
||
}
|
||
|
||
$html .= '<div style="margin-bottom:8px;padding:9px 12px;'
|
||
. 'background:rgba(255,255,255,.05);'
|
||
. 'border-left:3px solid #0099ff;'
|
||
. 'border-radius:0 6px 6px 0;">';
|
||
$html .= '📌 <b>' . esc_html( $title ) . '</b>';
|
||
$html .= $rendered;
|
||
$html .= '</div>';
|
||
}
|
||
|
||
// Zurück-Button zur Kategorieauswahl
|
||
$html .= '<br><div style="display:flex;gap:8px;flex-wrap:wrap;">';
|
||
$html .= '<button class="mm-quick-btn" data-q="regeln" type="button">← Zurück</button>';
|
||
$html .= '<a href="' . esc_url( $rules_url ) . '" target="_blank" '
|
||
. 'style="font-size:12px;color:#5bc0eb;align-self:center;">'
|
||
. 'Im Browser öffnen</a>';
|
||
$html .= '</div>';
|
||
|
||
return array(
|
||
'source' => 'rules',
|
||
'title' => '📜 ' . esc_html( $found_tab['title'] ) . ' – ' . count( $rules ) . ' Regeln',
|
||
'content' => $html,
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Volltext-Suche über alle Tabs + Regeln.
|
||
*/
|
||
function mm_rules_search( $q_clean, $tabs, $rules_url ) {
|
||
$q_lc = mb_strtolower( $q_clean );
|
||
$words = array_filter( preg_split( '/\s+/', $q_lc ), function( $w ) {
|
||
$stop = array( 'regel', 'regeln', 'ist', 'darf', 'ich', 'erlaubt', 'verboten', 'wie', 'was', 'kann' );
|
||
return strlen( $w ) > 2 && ! in_array( $w, $stop, true );
|
||
} );
|
||
|
||
$matches = array();
|
||
|
||
foreach ( $tabs as $tab ) {
|
||
$tab_title = $tab['title'] ?? 'Allgemein';
|
||
if ( empty( $tab['rules'] ) || ! is_array( $tab['rules'] ) ) continue;
|
||
|
||
foreach ( $tab['rules'] as $rule ) {
|
||
$r_title = mb_strtolower( $rule['rule_title'] ?? '' );
|
||
$r_content = mb_strtolower( wp_strip_all_tags( $rule['rule_content'] ?? '' ) );
|
||
$t_lc = mb_strtolower( $tab_title );
|
||
$score = 0;
|
||
|
||
if ( strpos( $r_title, $q_lc ) !== false ) $score += 30;
|
||
if ( strpos( $r_content, $q_lc ) !== false ) $score += 15;
|
||
if ( strpos( $t_lc, $q_lc ) !== false ) $score += 10;
|
||
|
||
foreach ( $words as $w ) {
|
||
if ( strpos( $r_title, $w ) !== false ) $score += 12;
|
||
if ( strpos( $r_content, $w ) !== false ) $score += 5;
|
||
if ( strpos( $t_lc, $w ) !== false ) $score += 4;
|
||
}
|
||
|
||
if ( $score > 0 ) {
|
||
$matches[] = array(
|
||
'score' => $score,
|
||
'tab' => $tab_title,
|
||
'title' => $rule['rule_title'] ?? '',
|
||
'content' => $rule['rule_content'] ?? '',
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( empty( $matches ) ) {
|
||
// Keine Treffer → direkt zur Kategorieauswahl
|
||
$html = 'Keine Regel zu <b>' . esc_html( $q_clean ) . '</b> gefunden.<br><br>';
|
||
$html .= '<small style="opacity:.7;">Wähle eine Kategorie:</small><br><br>';
|
||
$html .= '<div class="mm-bot-quick" style="flex-direction:column;gap:6px;">';
|
||
foreach ( $tabs as $tab ) {
|
||
$tab_title = $tab['title'] ?? 'Allgemein';
|
||
$html .= '<button class="mm-quick-btn" data-q="' . esc_attr( 'regeln:' . $tab_title ) . '" type="button">'
|
||
. '📂 ' . esc_html( $tab_title ) . '</button>';
|
||
}
|
||
$html .= '</div>';
|
||
|
||
return array(
|
||
'source' => 'rules',
|
||
'title' => '📜 Regelwerk',
|
||
'content' => $html,
|
||
);
|
||
}
|
||
|
||
usort( $matches, function( $a, $b ) { return $b['score'] - $a['score']; } );
|
||
$top = array_slice( $matches, 0, 3 );
|
||
$html = '';
|
||
|
||
foreach ( $top as $r ) {
|
||
$content_plain = wp_strip_all_tags( $r['content'] );
|
||
if ( mb_strlen( $content_plain ) > 300 ) {
|
||
$rendered = '<p style="margin:4px 0;font-size:13px;opacity:.9;">'
|
||
. esc_html( mb_substr( $content_plain, 0, 300 ) ) . '…</p>';
|
||
} else {
|
||
$rendered = '<div style="font-size:13px;line-height:1.6;margin-top:4px;opacity:.9;">'
|
||
. wp_kses_post( $r['content'] )
|
||
. '</div>';
|
||
}
|
||
|
||
$html .= '<div style="margin-bottom:8px;padding:9px 12px;'
|
||
. 'background:rgba(255,255,255,.05);'
|
||
. 'border-left:3px solid #0099ff;'
|
||
. 'border-radius:0 6px 6px 0;">';
|
||
$html .= '📌 <b>' . esc_html( $r['title'] ) . '</b>';
|
||
$html .= ' <small style="opacity:.6;">(' . esc_html( $r['tab'] ) . ')</small>';
|
||
$html .= $rendered;
|
||
$html .= '</div>';
|
||
}
|
||
|
||
if ( count( $matches ) > 3 ) {
|
||
$html .= '<small style="opacity:.7;">' . ( count( $matches ) - 3 ) . ' weitere Treffer vorhanden.</small><br>';
|
||
}
|
||
|
||
$html .= '<br><div style="display:flex;gap:8px;flex-wrap:wrap;">';
|
||
$html .= '<button class="mm-quick-btn" data-q="regeln" type="button">← Alle Kategorien</button>';
|
||
$html .= '<a href="' . esc_url( $rules_url ) . '" target="_blank" '
|
||
. 'style="font-size:12px;color:#5bc0eb;align-self:center;">Im Browser öffnen</a>';
|
||
$html .= '</div>';
|
||
|
||
return array(
|
||
'source' => 'rules',
|
||
'title' => '📜 Regelwerk – ' . count( $matches ) . ' Treffer',
|
||
'content' => $html,
|
||
);
|
||
}
|
||
|
||
// ============================================================
|
||
// INTENT: WIKI (WP Multi Wiki Plugin)
|
||
// ============================================================
|
||
|
||
function mm_intent_wiki( $q ) {
|
||
if ( ! post_type_exists( 'wmw_article' ) ) return null;
|
||
|
||
// Suchbegriff bereinigen
|
||
$search = preg_replace( '/\b(wiki|anleitung|guide|howto|how to|dokument|erkläre?|zeig mir)\b/i', '', $q );
|
||
$search = trim( preg_replace( '/\s+/', ' ', $search ) );
|
||
|
||
if ( strlen( $search ) < 2 ) {
|
||
// Alle Wikis auflisten
|
||
if ( function_exists( 'wmw_get_wikis' ) ) {
|
||
$wikis = wmw_get_wikis();
|
||
if ( ! empty( $wikis ) ) {
|
||
$links = array_map( function( $w ) {
|
||
return '→ <a href="' . esc_url( get_permalink( $w->ID ) ) . '">'
|
||
. esc_html( $w->post_title ) . '</a>';
|
||
}, $wikis );
|
||
return [
|
||
'source' => 'wiki',
|
||
'title' => '📖 Wiki',
|
||
'content' => 'Verfügbare Wikis:<br>' . implode( '<br>', $links ),
|
||
];
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
$args = [
|
||
'post_type' => 'wmw_article',
|
||
'post_status' => 'publish',
|
||
's' => $search,
|
||
'posts_per_page' => 4,
|
||
'orderby' => 'relevance',
|
||
];
|
||
$results = get_posts( $args );
|
||
|
||
if ( empty( $results ) ) {
|
||
// Fallback: alle Wiki-Artikel
|
||
$bot = get_option( 'mm_bot_data', [] );
|
||
$url = ! empty( $bot['url_wiki'] ) ? $bot['url_wiki'] : '';
|
||
$link = $url ? " → <a href='" . esc_url( $url ) . "' target='_blank'>Wiki öffnen</a>" : '';
|
||
return [
|
||
'source' => 'wiki',
|
||
'title' => '📖 Wiki',
|
||
'content' => 'Kein Wiki-Artikel zu „' . esc_html( $search ) . '“ gefunden.' . $link,
|
||
];
|
||
}
|
||
|
||
$html = [];
|
||
foreach ( $results as $post ) {
|
||
$excerpt = get_the_excerpt( $post->ID );
|
||
$html[] = '📄 <a href="' . esc_url( get_permalink( $post->ID ) ) . '"><b>'
|
||
. esc_html( $post->post_title ) . '</b></a>'
|
||
. ( $excerpt ? '<br><small>' . wp_trim_words( $excerpt, 25, '…' ) . '</small>' : '' );
|
||
}
|
||
|
||
return [
|
||
'source' => 'wiki',
|
||
'title' => '📖 Wiki-Treffer',
|
||
'content' => implode( '<br><br>', $html ),
|
||
];
|
||
}
|
||
|
||
// ============================================================
|
||
// INTENT: SHOP (WP Ingame Shop Pro)
|
||
// ============================================================
|
||
|
||
function mm_intent_shop( $q_lc, $bot ) {
|
||
global $wpdb;
|
||
$t_items = $wpdb->prefix . 'wis_items';
|
||
$t_cats = $wpdb->prefix . 'wis_categories';
|
||
|
||
if ( $wpdb->get_var( "SHOW TABLES LIKE '$t_items'" ) !== $t_items ) return null;
|
||
|
||
// DEBUG: Suchbegriff und Item-Anzahl loggen
|
||
if (!function_exists('mm_debug_log')) {
|
||
function mm_debug_log($msg) {
|
||
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||
error_log('[MM SHOP] ' . $msg);
|
||
}
|
||
}
|
||
}
|
||
|
||
mm_debug_log('Suchbegriff: ' . $q_lc);
|
||
|
||
$currency = get_option( 'wis_currency_name', 'Coins' );
|
||
$lines = [];
|
||
$img_base = get_option( 'wis_image_base_url', '' );
|
||
$shop_url = $bot['url_shop'] ?? '';
|
||
// Shop-Antwort-Box (grau, Card-Layout)
|
||
// $lines[] = '<div style="width:100%;max-width:none;background:#40444b;color:#dcddde;padding:0;margin:0;border-radius:0;text-align:center;">';
|
||
|
||
// ── Tagesaktion nur anzeigen, wenn KEINE Preisabfrage ─────────────────
|
||
$is_price_query = false;
|
||
$search = $q_lc;
|
||
if (preg_match('/\b(was kostet|preis von|wie teuer|wie viel kostet|kosten von|preis für|preis)\b/i', $search)) {
|
||
$is_price_query = true;
|
||
}
|
||
if (!$is_price_query) {
|
||
$deal = $wpdb->get_row( "SELECT * FROM $t_items WHERE is_daily_deal = 1 AND status = 'publish' LIMIT 1" );
|
||
if ( $deal ) {
|
||
$offer = isset( $deal->offer_price ) && $deal->offer_price > 0 ? $deal->offer_price : $deal->price;
|
||
$img = $img_base . str_replace(':', '_', $deal->item_id) . '.png';
|
||
$item_link = $shop_url ? esc_url($shop_url) . '#item-' . urlencode($deal->item_id) : '';
|
||
$lines[] = '<div style="text-align:center; margin:8px 0 12px 0;">'
|
||
. '🔥 <b>Tagesaktion:</b><br>'
|
||
. '<img src="' . esc_url($img) . '" alt="' . esc_attr($deal->name) . '" style="width:64px;height:64px;display:block;margin:0 auto 6px auto;">'
|
||
. '<div style="font-size:1.1em;font-weight:bold;margin-bottom:2px;">' . esc_html( $deal->name ) . '</div>'
|
||
. '<span style="font-size:1.1em;">'
|
||
. '<b>' . esc_html( (string) $offer ) . ' ' . esc_html( $currency ) . '</b>'
|
||
. ( $deal->price != $offer ? ' <s>' . esc_html( (string) $deal->price ) . '</s>' : '' )
|
||
. '</span>'
|
||
. ( $item_link ? '<br>→ <a href="' . $item_link . '" target="_blank">Zum Item im Shop</a>' : '' )
|
||
. '</div>';
|
||
}
|
||
}
|
||
|
||
// ── Suchbegriff extrahieren (auch für Preisabfragen) ────────────────
|
||
// (is_price_query wurde oben schon gesetzt)
|
||
$search = $q_lc;
|
||
if ($is_price_query) {
|
||
// Nur das Item extrahieren
|
||
$search = preg_replace('/\b(shop|kaufen|kosten|preis|items?|angebot|tagesaktion|daily deal|kategorie|rang|was gibt|zeig|liste|was kostet|preis von|wie teuer|wie viel kostet|kosten von|preis für)\b/i', '', $search);
|
||
} else {
|
||
$search = preg_replace('/\b(shop|kaufen|kosten|preis|items?|angebot|tagesaktion|daily deal|kategorie|rang|was gibt|zeig|liste)\b/i', '', $search);
|
||
}
|
||
$search = trim(preg_replace('/\s+/', ' ', $search));
|
||
// Satzzeichen am Anfang/Ende entfernen
|
||
$search = preg_replace('/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/u', '', $search);
|
||
|
||
if (strlen($search) >= 3) {
|
||
mm_debug_log('Suchbegriff nach Extraktion: ' . $search);
|
||
// Spezifische Suche (auch für Preisabfragen)
|
||
$items = $wpdb->get_results($wpdb->prepare(
|
||
"SELECT * FROM $t_items
|
||
WHERE status = 'publish' AND (item_title LIKE %s OR name LIKE %s OR item_id LIKE %s)
|
||
ORDER BY price ASC LIMIT 8",
|
||
'%' . $wpdb->esc_like($search) . '%',
|
||
'%' . $wpdb->esc_like($search) . '%',
|
||
'%' . $wpdb->esc_like($search) . '%'
|
||
));
|
||
mm_debug_log('LIKE-Query: ' . $search . ' | Treffer: ' . count($items));
|
||
|
||
if (!empty($items)) {
|
||
mm_debug_log('LIKE-Query erstes Item: ' . (isset($items[0]) ? json_encode($items[0]) : 'n/a'));
|
||
// Preisabfrage: Nur das erste Item als Preisantwort
|
||
if ($is_price_query) {
|
||
$item = $items[0];
|
||
$img = $img_base . str_replace(':', '_', $item->item_id) . '.png';
|
||
$item_link = $shop_url ? esc_url($shop_url) . '#item-' . urlencode($item->item_id) : '';
|
||
$item_display_name = $item->name ?: ($item->item_title ?: $item->item_id);
|
||
// Serverliste aufbereiten
|
||
$server_list = '';
|
||
if (!empty($item->servers)) {
|
||
$servers = json_decode($item->servers, true);
|
||
if (is_array($servers) && count($servers) > 0) {
|
||
$server_list = '<div style="font-size:0.97em;color:#888;margin-top:6px;">Verfügbar auf: <span style=\'color:#444;\'>' . esc_html(implode(', ', $servers)) . '</span></div>';
|
||
}
|
||
}
|
||
$lines[] = '<div style="max-width:320px;margin:18px auto 12px auto;padding:14px 16px 12px 16px;background:#ececf1;border-radius:16px;box-shadow:0 2px 12px rgba(0,0,0,0.08);display:flex;flex-direction:column;align-items:center;">'
|
||
. '<div style="font-size:1.05em;font-weight:600;color:#6b7280;margin-bottom:10px;text-align:center;">🛒 Shop-Item:</div>'
|
||
. '<img src="' . esc_url($img) . '" alt="' . esc_attr($item_display_name) . '" style="width:56px;height:56px;border-radius:8px;background:#e5e7eb;box-shadow:0 1px 4px rgba(0,0,0,0.06);margin-bottom:12px;">'
|
||
. '<div style="font-size:1.13em;font-weight:700;color:#23272e;margin-bottom:6px;text-align:center;">' . esc_html($item_display_name) . '</div>'
|
||
. '<div style="font-size:1.12em;font-weight:700;color:#22c55e;margin-bottom:2px;text-align:center;">' . esc_html((string)$item->price) . ' ' . esc_html($currency) . '</div>'
|
||
. $server_list
|
||
. '</div>';
|
||
} else {
|
||
$lines[] = '🛒 <b>Gefundene Items:</b>';
|
||
foreach ($items as $item) {
|
||
$img = $img_base . str_replace(':', '_', $item->item_id) . '.png';
|
||
$item_link = $shop_url ? esc_url($shop_url) . '#item-' . urlencode($item->item_id) : '';
|
||
$lines[] = '<img src="' . esc_url($img) . '" alt="' . esc_attr($item->item_title) . '" style="width:48px;height:48px;vertical-align:middle;margin-right:8px;">'
|
||
. '<b>' . esc_html($item->item_title) . '</b>'
|
||
. ' – ' . esc_html((string)$item->price) . ' ' . esc_html($currency)
|
||
. ($item_link ? ' <a href="' . $item_link . '" target="_blank">[Shop]</a>' : '');
|
||
}
|
||
}
|
||
} else {
|
||
// Exakte Suche, falls LIKE nichts findet
|
||
$item = $wpdb->get_row($wpdb->prepare(
|
||
"SELECT * FROM $t_items WHERE status = 'publish' AND (name = %s OR item_id = %s OR item_title = %s) LIMIT 1",
|
||
$search, $search, $search
|
||
));
|
||
mm_debug_log('Exakte Suche: ' . ($item ? json_encode($item) : 'kein Treffer'));
|
||
// Case-insensitive Fallback, falls nötig
|
||
if (!$item && !empty($search)) {
|
||
$item = $wpdb->get_row($wpdb->prepare(
|
||
"SELECT * FROM $t_items WHERE status = 'publish' AND (name COLLATE utf8_general_ci = %s COLLATE utf8_general_ci OR item_id COLLATE utf8_general_ci = %s COLLATE utf8_general_ci OR item_title COLLATE utf8_general_ci = %s COLLATE utf8_general_ci) LIMIT 1",
|
||
$search, $search, $search
|
||
));
|
||
mm_debug_log('Case-insensitive Suche: ' . ($item ? json_encode($item) : 'kein Treffer'));
|
||
}
|
||
// Fallback: Suchbegriff als Minecraft-ID mit Prefix testen
|
||
if (!$item && !empty($search) && strpos($search, 'minecraft:') === false) {
|
||
$mc_id = 'minecraft:' . strtolower(str_replace([' ', '_'], ['_', '_'], $search));
|
||
$item = $wpdb->get_row($wpdb->prepare(
|
||
"SELECT * FROM $t_items WHERE status = 'publish' AND (item_id = %s OR item_id COLLATE utf8_general_ci = %s COLLATE utf8_general_ci) LIMIT 1",
|
||
$mc_id, $mc_id
|
||
));
|
||
mm_debug_log('MC-ID Fallback: ' . $mc_id . ' | ' . ($item ? json_encode($item) : 'kein Treffer'));
|
||
}
|
||
if ($item) {
|
||
mm_debug_log('Gefundenes Item (exakt/fallback): ' . json_encode($item));
|
||
$img = $img_base . str_replace(':', '_', $item->item_id) . '.png';
|
||
$item_link = $shop_url ? esc_url($shop_url) . '#item-' . urlencode($item->item_id) : '';
|
||
$lines[] = '<div style="display:flex;align-items:center;gap:10px;margin-bottom:6px;">'
|
||
. '<img src="' . esc_url($img) . '" alt="' . esc_attr($item->name) . '" style="width:48px;height:48px;">'
|
||
. '<div><b>' . esc_html($item->name) . '</b><br>'
|
||
. 'Preis: <b>' . esc_html((string)$item->price) . ' ' . esc_html($currency) . '</b>'
|
||
. ($item_link ? ' <a href="' . $item_link . '" target="_blank">[Shop]</a>' : '')
|
||
. '</div></div>';
|
||
} else {
|
||
// Aggressive Fuzzy-Suche als letzter Versuch
|
||
$all_items = $wpdb->get_results("SELECT * FROM $t_items WHERE status = 'publish'");
|
||
mm_debug_log('Fuzzy-Suche über alle Items (' . count($all_items) . '): ' . $search);
|
||
$search_lc = mb_strtolower($search);
|
||
$fuzzy = null;
|
||
foreach ($all_items as $it) {
|
||
if (strpos(mb_strtolower($it->name), $search_lc) !== false
|
||
|| strpos(mb_strtolower($it->item_title), $search_lc) !== false
|
||
|| strpos(mb_strtolower($it->item_id), $search_lc) !== false) {
|
||
$fuzzy = $it;
|
||
break;
|
||
}
|
||
}
|
||
mm_debug_log('Fuzzy-Suche Treffer: ' . ($fuzzy ? json_encode($fuzzy) : 'kein Treffer'));
|
||
if ($fuzzy) {
|
||
$img = $img_base . str_replace(':', '_', $fuzzy->item_id) . '.png';
|
||
$item_link = $shop_url ? esc_url($shop_url) . '#item-' . urlencode($fuzzy->item_id) : '';
|
||
$item_display_name = $fuzzy->name ?: ($fuzzy->item_title ?: $fuzzy->item_id);
|
||
// Serverliste aufbereiten
|
||
$server_list = '';
|
||
if (!empty($fuzzy->servers)) {
|
||
$servers = json_decode($fuzzy->servers, true);
|
||
if (is_array($servers) && count($servers) > 0) {
|
||
$server_list = '<div style="font-size:0.97em;color:#888;margin-top:6px;">Verfügbar auf: <span style=\'color:#444;\'>' . esc_html(implode(', ', $servers)) . '</span></div>';
|
||
}
|
||
}
|
||
$lines[] = '<div style="max-width:320px;margin:18px auto 12px auto;padding:14px 16px 12px 16px;background:#ececf1;border-radius:16px;box-shadow:0 2px 12px rgba(0,0,0,0.08);display:flex;flex-direction:column;align-items:center;">'
|
||
. '<div style="font-size:1.05em;font-weight:600;color:#6b7280;margin-bottom:10px;text-align:center;">🛒 Shop-Item:</div>'
|
||
. '<img src="' . esc_url($img) . '" alt="' . esc_attr($item_display_name) . '" style="width:56px;height:56px;border-radius:8px;background:#e5e7eb;box-shadow:0 1px 4px rgba(0,0,0,0.06);margin-bottom:12px;">'
|
||
. '<div style="font-size:1.13em;font-weight:700;color:#23272e;margin-bottom:6px;text-align:center;">' . esc_html($item_display_name) . '</div>'
|
||
. '<div style="font-size:1.12em;font-weight:700;color:#22c55e;margin-bottom:2px;text-align:center;">' . esc_html((string)$fuzzy->price) . ' ' . esc_html($currency) . '</div>'
|
||
. $server_list
|
||
. '</div>';
|
||
} else {
|
||
$lines[] = 'Kein Item mit „' . esc_html($search) . '“ gefunden.';
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
// Übersicht: Kategorien + Item-Anzahl
|
||
$total = (int) $wpdb->get_var( "SELECT COUNT(*) FROM $t_items WHERE status = 'publish'" );
|
||
$lines[] = '🛒 <b>Shop-Übersicht:</b> <b>' . $total . ' Items</b> verfügbar.';
|
||
|
||
$cats = $wpdb->get_results(
|
||
"SELECT c.name, COUNT(i.id) as cnt
|
||
FROM $t_cats c
|
||
LEFT JOIN $t_items i ON i.category_id = c.id AND i.status = 'publish'
|
||
GROUP BY c.id ORDER BY cnt DESC LIMIT 8"
|
||
);
|
||
if ( ! empty( $cats ) ) {
|
||
$lines[] = '📂 <b>Kategorien:</b>';
|
||
foreach ( $cats as $cat ) {
|
||
$lines[] = ' • ' . esc_html( $cat->name ) . ' (' . intval( $cat->cnt ) . ' Items)';
|
||
}
|
||
}
|
||
|
||
// Günstigste & teuerste Items
|
||
$cheapest = $wpdb->get_row( "SELECT item_title, price FROM $t_items WHERE status = 'publish' AND price > 0 ORDER BY price ASC LIMIT 1" );
|
||
$priciest = $wpdb->get_row( "SELECT item_title, price FROM $t_items WHERE status = 'publish' ORDER BY price DESC LIMIT 1" );
|
||
if ( $cheapest ) $lines[] = '💚 Günstigstes: <b>' . esc_html( $cheapest->item_title ) . '</b> – ' . esc_html( (string) $cheapest->price ) . ' ' . esc_html( $currency );
|
||
if ( $priciest ) $lines[] = '💎 Teuerstes: <b>' . esc_html( $priciest->item_title ) . '</b> – ' . esc_html( (string) $priciest->price ) . ' ' . esc_html( $currency );
|
||
}
|
||
|
||
if ( $shop_url ) {
|
||
$lines[] = "<div style='text-align:center;margin:10px 0 0 0;'><a href='" . esc_url( $shop_url ) . "' target='_blank' style='color:#2563eb;font-size:1em;text-decoration:underline;font-weight:500;'>→ Zum Item im Shop</a></div>";
|
||
}
|
||
|
||
return [
|
||
'source' => 'shop',
|
||
'title' => '🛒 Shop',
|
||
'content' => implode( '<br>', $lines ),
|
||
];
|
||
}
|
||
|
||
// ============================================================
|
||
// INTENT: TICKET (WP Multi Ticket Pro)
|
||
// ============================================================
|
||
|
||
function mm_intent_ticket( $q, $bot ) {
|
||
global $wpdb;
|
||
$table = $wpdb->prefix . 'wmt_tickets';
|
||
$table_msg = $wpdb->prefix . 'wmt_messages';
|
||
$exists = $wpdb->get_var( "SHOW TABLES LIKE '$table'" ) === $table;
|
||
|
||
// ── Ticket per Nummer nachschlagen ────────────────────────
|
||
if ( preg_match( '/#?(\d{3,6})\b/', $q, $m ) && $exists ) {
|
||
$ticket_id = intval( $m[1] );
|
||
$ticket = $wpdb->get_row( $wpdb->prepare(
|
||
"SELECT id, subject, status, category, department, created_at, guest_name, guest_email
|
||
FROM $table WHERE id = %d",
|
||
$ticket_id
|
||
) );
|
||
|
||
if ( $ticket ) {
|
||
// Letzte Nachricht
|
||
$last_msg = '';
|
||
if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_msg'" ) === $table_msg ) {
|
||
$msg = $wpdb->get_row( $wpdb->prepare(
|
||
"SELECT message, created_at FROM $table_msg WHERE ticket_id = %d ORDER BY created_at DESC LIMIT 1",
|
||
$ticket_id
|
||
) );
|
||
if ( $msg ) {
|
||
$last_msg = '<br>💬 Letzte Nachricht: <i>'
|
||
. esc_html( wp_trim_words( $msg->message, 20, '…' ) )
|
||
. '</i> <small>(' . wp_date( 'd.m.Y H:i', strtotime( $msg->created_at ) ) . ')</small>';
|
||
}
|
||
}
|
||
|
||
$lines = [
|
||
'🎫 Ticket <b>#' . $ticket->id . '</b>',
|
||
'📋 Betreff: <b>' . esc_html( $ticket->subject ) . '</b>',
|
||
'📌 Status: <b>' . esc_html( $ticket->status ) . '</b>',
|
||
'🏷️ Kategorie: ' . esc_html( $ticket->category ?: '–' ),
|
||
];
|
||
if ( ! empty( $ticket->department ) ) {
|
||
$lines[] = '🏢 Abteilung: ' . esc_html( $ticket->department );
|
||
}
|
||
if ( ! empty( $ticket->guest_name ) ) {
|
||
$lines[] = '👤 Von: ' . esc_html( $ticket->guest_name );
|
||
}
|
||
$lines[] = '📅 Erstellt: ' . wp_date( 'd.m.Y \u\m H:i', strtotime( $ticket->created_at ) );
|
||
if ( $last_msg ) $lines[] = $last_msg;
|
||
|
||
return [
|
||
'source' => 'ticket',
|
||
'title' => '🎫 Ticket #' . $ticket->id,
|
||
'content' => implode( '<br>', $lines ),
|
||
];
|
||
} else {
|
||
return [
|
||
'source' => 'ticket',
|
||
'title' => '🎫 Ticket nicht gefunden',
|
||
'content' => 'Ticket <b>#' . $ticket_id . '</b> existiert nicht.',
|
||
];
|
||
}
|
||
}
|
||
|
||
// ── Allgemeine Ticket-Übersicht ────────────────────────────
|
||
$lines = [];
|
||
|
||
if ( $exists ) {
|
||
$open = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $table WHERE status NOT LIKE %s", '%Geschlossen%' ) );
|
||
$closed = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $table WHERE status LIKE %s", '%Geschlossen%' ) );
|
||
$total = $open + $closed;
|
||
|
||
$lines[] = '📊 Gesamt: <b>' . $total . '</b> Tickets · Offen: <b>' . $open . '</b> · Geschlossen: <b>' . $closed . '</b>';
|
||
|
||
// Kategorien anzeigen
|
||
$cats = $wpdb->get_results(
|
||
"SELECT COALESCE(category, 'Keine Kategorie') as cat, COUNT(*) as cnt
|
||
FROM $table GROUP BY category ORDER BY cnt DESC LIMIT 5"
|
||
);
|
||
if ( ! empty( $cats ) ) {
|
||
$cat_parts = [];
|
||
foreach ( $cats as $c ) {
|
||
$cat_parts[] = esc_html( $c->cat ) . ' (' . intval( $c->cnt ) . ')';
|
||
}
|
||
$lines[] = '📂 <b>Kategorien:</b> ' . implode( ' · ', $cat_parts );
|
||
}
|
||
}
|
||
|
||
$ticket_url = $bot['url_tickets'] ?? '';
|
||
if ( $ticket_url ) {
|
||
$lines[] = "→ <a href='" . esc_url( $ticket_url ) . "' target='_blank'><b>Neues Ticket erstellen</b></a>";
|
||
$lines[] = '<small>Oder gib eine Ticket-Nummer ein, z.B.: <b>#1234</b></small>';
|
||
} else {
|
||
$lines[] = 'Bitte nutze das Support-Formular auf unserer Website.';
|
||
}
|
||
|
||
return [
|
||
'source' => 'ticket',
|
||
'title' => '🎫 Support-Ticket',
|
||
'content' => implode( '<br>', $lines ),
|
||
];
|
||
}
|
||
|
||
// ============================================================
|
||
// INTENT: FORUM (WP Business Forum)
|
||
// ============================================================
|
||
|
||
function mm_intent_forum( $q ) {
|
||
// Nur aktiv wenn WBF installiert UND URL im Backend gesetzt
|
||
$bot = get_option( 'mm_bot_data', [] );
|
||
if ( ! class_exists( 'WBF_DB' ) ) return null;
|
||
if ( empty( $bot['url_forum'] ) ) return null;
|
||
|
||
global $wpdb;
|
||
|
||
$forum_url = esc_url( $bot['url_forum'] );
|
||
$q_lc = mb_strtolower( $q );
|
||
|
||
// Tabellen prüfen — fehlen sie, früh abbrechen statt hängen
|
||
$t_tbl = $wpdb->prefix . 'forum_threads';
|
||
$p_tbl = $wpdb->prefix . 'forum_posts';
|
||
$u_tbl = $wpdb->prefix . 'forum_users';
|
||
$c_tbl = $wpdb->prefix . 'forum_categories';
|
||
|
||
$tables_ok = (
|
||
$wpdb->get_var( "SHOW TABLES LIKE '$t_tbl'" ) === $t_tbl &&
|
||
$wpdb->get_var( "SHOW TABLES LIKE '$u_tbl'" ) === $u_tbl &&
|
||
$wpdb->get_var( "SHOW TABLES LIKE '$c_tbl'" ) === $c_tbl
|
||
);
|
||
|
||
// ── Sub-Intent: Registrierung ─────────────────────────────
|
||
if ( preg_match( '/\b(registrier(en|ung)?|konto erstellen|account erstellen|sign.?up|einschreiben)\b/i', $q ) ) {
|
||
$lines = [];
|
||
if ( $tables_ok ) {
|
||
try {
|
||
$reg_mode = get_option( 'wbf_settings', [] )['registration_mode'] ?? 'open';
|
||
$member_count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM $u_tbl WHERE role != 'banned'" );
|
||
|
||
$lines[] = '📝 <b>Registrierung:</b>';
|
||
if ( $reg_mode === 'disabled' ) {
|
||
$lines[] = '❌ Die Registrierung ist derzeit <b>deaktiviert</b>.';
|
||
} elseif ( $reg_mode === 'invite' ) {
|
||
$lines[] = '🔒 Das Forum nutzt <b>Einladungscodes</b>. Du benötigst einen gültigen Code.';
|
||
} else {
|
||
$lines[] = '✅ Die Registrierung ist <b>offen</b> — jederzeit möglich.';
|
||
}
|
||
if ( $member_count > 0 ) {
|
||
$lines[] = '<small>👥 Bereits <b>' . $member_count . '</b> Mitglieder dabei.</small>';
|
||
}
|
||
} catch ( \Throwable $e ) {
|
||
$lines[] = '📝 Registrierung verfügbar.';
|
||
}
|
||
} else {
|
||
$lines[] = '📝 <b>Registrierung:</b>';
|
||
$lines[] = 'Erstelle deinen Account direkt im Forum.';
|
||
}
|
||
$lines[] = "→ <a href='{$forum_url}' target='_blank'><b>Zum Forum & Registrierung</b></a>";
|
||
return [
|
||
'source' => 'forum',
|
||
'title' => '💬 Forum-Registrierung',
|
||
'content' => implode( '<br>', $lines ),
|
||
];
|
||
}
|
||
|
||
// ── Sub-Intent: Login / Passwort ──────────────────────────
|
||
if ( preg_match( '/\b(login|einloggen|passwort vergessen|passwort.?reset|zugangsdaten)\b/i', $q ) ) {
|
||
$lines = [];
|
||
$lines[] = '🔑 <b>Forum-Login:</b>';
|
||
$lines[] = "→ <a href='{$forum_url}' target='_blank'><b>Zum Forum & Login</b></a>";
|
||
if ( preg_match( '/passwort/i', $q ) ) {
|
||
$lines[] = '<br>🔓 <b>Passwort vergessen?</b>';
|
||
$lines[] = 'Klicke auf der Login-Seite auf <em>„Passwort vergessen"</em>.';
|
||
}
|
||
return [
|
||
'source' => 'forum',
|
||
'title' => '💬 Forum-Login',
|
||
'content' => implode( '<br>', $lines ),
|
||
];
|
||
}
|
||
|
||
// ── Sub-Intent: Profil ────────────────────────────────────
|
||
if ( preg_match( '/\b(profil|mein konto|mein account|avatar|signatur|forum rang|forum level)\b/i', $q ) ) {
|
||
$lines = [];
|
||
$lines[] = '👤 <b>Dein Forum-Profil</b> enthält:';
|
||
$lines[] = ' • Anzeigename, Avatar & Signatur';
|
||
if ( class_exists( 'WBF_Levels' ) && WBF_Levels::is_enabled() ) {
|
||
$lines[] = ' • Level-System (basiert auf Beitragsanzahl)';
|
||
}
|
||
$lines[] = ' • Rollen-Badge, Beitragsanzahl & Registrierungsdatum';
|
||
$lines[] = "→ <a href='{$forum_url}' target='_blank'><b>Zum Forum → Profil öffnen</b></a>";
|
||
return [
|
||
'source' => 'forum',
|
||
'title' => '💬 Forum-Profil',
|
||
'content' => implode( '<br>', $lines ),
|
||
];
|
||
}
|
||
|
||
// ── Sub-Intent: Nachrichten / DM ──────────────────────────
|
||
if ( preg_match( '/\b(direktnachricht|forum nachricht|forum inbox|pn schreiben|privat.?nachricht)\b/i', $q ) ) {
|
||
$lines = [];
|
||
$lines[] = '✉️ <b>Direktnachrichten</b> im Forum:';
|
||
$lines[] = 'Du kannst eingeloggten Mitgliedern direkt Nachrichten schreiben.';
|
||
$lines[] = "→ <a href='{$forum_url}' target='_blank'><b>Forum öffnen → Postfach</b></a>";
|
||
return [
|
||
'source' => 'forum',
|
||
'title' => '💬 Forum-Nachrichten',
|
||
'content' => implode( '<br>', $lines ),
|
||
];
|
||
}
|
||
|
||
// ── Sub-Intent: Thread erstellen ──────────────────────────
|
||
if ( preg_match( '/\b(neuen thread|thread erstellen|beitrag erstellen|topic erstellen|im forum schreiben|im forum posten)\b/i', $q ) ) {
|
||
$lines = [];
|
||
$lines[] = '✏️ <b>Neuen Thread erstellen:</b>';
|
||
$lines[] = '① Melde dich im Forum an.';
|
||
$lines[] = '② Wähle die passende Kategorie.';
|
||
$lines[] = '③ Klicke auf <em>„Neuen Thread"</em> und fülle Titel & Inhalt aus.';
|
||
$lines[] = "→ <a href='{$forum_url}' target='_blank'><b>Forum öffnen</b></a>";
|
||
return [
|
||
'source' => 'forum',
|
||
'title' => '💬 Thread erstellen',
|
||
'content' => implode( '<br>', $lines ),
|
||
];
|
||
}
|
||
|
||
// ── Suchbegriff extrahieren ────────────────────────────────
|
||
$search = preg_replace( '/\b(forum|diskussion|thread|thema|beitrag|post|kategorie|suche?)\b/i', '', $q );
|
||
$search = trim( preg_replace( '/\s+/', ' ', $search ) );
|
||
|
||
// ── Thread-Suche ──────────────────────────────────────────
|
||
if ( strlen( $search ) >= 3 && $tables_ok ) {
|
||
try {
|
||
$threads = $wpdb->get_results( $wpdb->prepare(
|
||
"SELECT t.id, t.title, t.slug, t.reply_count, t.view_count, t.is_pinned,
|
||
c.name as cat_name
|
||
FROM {$t_tbl} t
|
||
LEFT JOIN {$c_tbl} c ON t.category_id = c.id
|
||
WHERE t.title LIKE %s AND t.deleted_at IS NULL
|
||
ORDER BY t.is_pinned DESC, t.created_at DESC LIMIT 5",
|
||
'%' . $wpdb->esc_like( $search ) . '%'
|
||
) );
|
||
|
||
if ( ! empty( $threads ) ) {
|
||
$html = '';
|
||
foreach ( $threads as $t ) {
|
||
$t_url = $forum_url . '?thread=' . esc_attr( $t->slug );
|
||
$pin = $t->is_pinned ? '📌 ' : '';
|
||
$meta = [];
|
||
if ( $t->reply_count > 0 ) $meta[] = $t->reply_count . ' Antworten';
|
||
if ( $t->view_count > 0 ) $meta[] = $t->view_count . ' Aufrufe';
|
||
$title_short = mb_strlen( $t->title ) > 50 ? mb_substr( $t->title, 0, 50 ) . '…' : $t->title;
|
||
$html .= '<div style="margin-bottom:6px;padding:7px 10px;background:rgba(255,255,255,.04);border-left:2px solid #0099ff;border-radius:0 5px 5px 0;">';
|
||
$html .= $pin . '<a href="' . esc_url( $t_url ) . '" target="_blank"><b>' . esc_html( $title_short ) . '</b></a>';
|
||
if ( $t->cat_name ) $html .= ' <small style="opacity:.6;">· ' . esc_html( $t->cat_name ) . '</small>';
|
||
if ( ! empty( $meta ) ) $html .= '<br><small style="opacity:.55;">' . implode( ' · ', $meta ) . '</small>';
|
||
$html .= '</div>';
|
||
}
|
||
$html .= "<br>→ <a href='{$forum_url}' target='_blank'>Alle Threads im Forum</a>";
|
||
return [
|
||
'source' => 'forum',
|
||
'title' => '💬 Forum – ' . count( $threads ) . ' Treffer',
|
||
'content' => $html,
|
||
];
|
||
}
|
||
|
||
return [
|
||
'source' => 'forum',
|
||
'title' => '💬 Forum',
|
||
'content' => 'Kein Thread zu <b>' . esc_html( $search ) . '</b> gefunden.<br>'
|
||
. "→ <a href='{$forum_url}' target='_blank'>Forum öffnen & selbst suchen</a>",
|
||
];
|
||
} catch ( \Throwable $e ) {
|
||
// Bei DB-Fehler: einfach Übersicht zeigen
|
||
}
|
||
}
|
||
|
||
// ── Allgemeine Übersicht mit Live-Statistiken ──────────────
|
||
$lines = [];
|
||
|
||
if ( $tables_ok ) {
|
||
try {
|
||
$total_threads = (int) $wpdb->get_var( "SELECT COUNT(*) FROM $t_tbl WHERE deleted_at IS NULL" );
|
||
$total_posts = (int) $wpdb->get_var( "SELECT COUNT(*) FROM $p_tbl WHERE deleted_at IS NULL" );
|
||
$total_members = (int) $wpdb->get_var( "SELECT COUNT(*) FROM $u_tbl WHERE role != 'banned'" );
|
||
$online_count = (int) $wpdb->get_var(
|
||
"SELECT COUNT(*) FROM $u_tbl WHERE last_active >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)"
|
||
);
|
||
|
||
$stat_parts = [];
|
||
if ( $total_threads > 0 ) $stat_parts[] = '<b>' . $total_threads . '</b> Threads';
|
||
if ( $total_posts > 0 ) $stat_parts[] = '<b>' . $total_posts . '</b> Beiträge';
|
||
if ( $total_members > 0 ) $stat_parts[] = '<b>' . $total_members . '</b> Mitglieder';
|
||
if ( ! empty( $stat_parts ) ) {
|
||
$lines[] = '📊 ' . implode( ' · ', $stat_parts );
|
||
}
|
||
|
||
$online_icon = $online_count > 0 ? '🟢' : '⚫';
|
||
$lines[] = $online_icon . ' ' . ( $online_count > 0
|
||
? '<b>' . $online_count . '</b> Mitglied' . ( $online_count > 1 ? 'er' : '' ) . ' gerade online'
|
||
: 'Aktuell niemand aktiv' );
|
||
|
||
// Kategorien
|
||
$cats = $wpdb->get_results(
|
||
"SELECT c.name, c.slug, COUNT(t.id) as thread_count
|
||
FROM {$c_tbl} c
|
||
LEFT JOIN {$t_tbl} t ON t.category_id = c.id AND t.deleted_at IS NULL
|
||
WHERE c.parent_id = 0
|
||
GROUP BY c.id ORDER BY c.sort_order ASC LIMIT 6"
|
||
);
|
||
if ( ! empty( $cats ) ) {
|
||
$lines[] = '<br>📂 <b>Kategorien:</b>';
|
||
foreach ( $cats as $cat ) {
|
||
$c_url = $forum_url . '?cat=' . esc_attr( $cat->slug );
|
||
$count = $cat->thread_count > 0 ? ' <small style="opacity:.55;">(' . (int) $cat->thread_count . ')</small>' : '';
|
||
$lines[] = ' → <a href="' . esc_url( $c_url ) . '" target="_blank">'
|
||
. esc_html( $cat->name ) . '</a>' . $count;
|
||
}
|
||
}
|
||
|
||
// Neueste Threads
|
||
$recent = $wpdb->get_results(
|
||
"SELECT t.title, t.slug, t.reply_count
|
||
FROM {$t_tbl} t
|
||
WHERE t.deleted_at IS NULL AND t.is_pinned = 0
|
||
ORDER BY t.created_at DESC LIMIT 3"
|
||
);
|
||
if ( ! empty( $recent ) ) {
|
||
$lines[] = '<br>🆕 <b>Neueste Threads:</b>';
|
||
foreach ( $recent as $r ) {
|
||
$t_url = $forum_url . '?thread=' . esc_attr( $r->slug );
|
||
$title_short = mb_strlen( $r->title ) > 50 ? mb_substr( $r->title, 0, 50 ) . '…' : $r->title;
|
||
$replies = $r->reply_count > 0 ? ' <small style="opacity:.55;">(' . (int) $r->reply_count . ' Antw.)</small>' : '';
|
||
$lines[] = ' → <a href="' . esc_url( $t_url ) . '" target="_blank">'
|
||
. esc_html( $title_short ) . '</a>' . $replies;
|
||
}
|
||
}
|
||
} catch ( \Throwable $e ) {
|
||
$lines[] = '⚠️ Forum-Daten konnten nicht geladen werden.';
|
||
}
|
||
}
|
||
|
||
$lines[] = ( ! empty( $lines ) ? '<br>' : '' )
|
||
. "→ <a href='{$forum_url}' target='_blank'><b>Forum öffnen</b></a>";
|
||
|
||
return [
|
||
'source' => 'forum',
|
||
'title' => '💬 Forum',
|
||
'content' => implode( '<br>', $lines ),
|
||
];
|
||
}
|
||
|
||
// ============================================================
|
||
// INTENT: GALERIE (MC MultiServer Gallery PRO)
|
||
// ============================================================
|
||
|
||
function mm_intent_gallery( $bot, $q_lc = '' ) {
|
||
$gallery_url = ! empty( $bot['url_gallery'] ) ? $bot['url_gallery'] : home_url( '/galerie' );
|
||
|
||
// ── Erkennen ob Upload-Frage ──────────────────────────────
|
||
$is_upload_query = (bool) preg_match(
|
||
'/hochlad|upload|uploaden|verifizier|verify|token|bild teilen|bilder teilen|wie.*bild|anleitung.*galerie/i',
|
||
$q_lc
|
||
);
|
||
|
||
$lines = [];
|
||
|
||
// ── 1. Galerie-Link ──────────────────────────────────────
|
||
$lines[] = "📷 → <a href='" . esc_url( $gallery_url ) . "' target='_blank'><b>Galerie öffnen</b></a>";
|
||
|
||
// ── 2. Upload-Anleitung (bei Upload-Frage oder allgemein) ─
|
||
if ( $is_upload_query ) {
|
||
$lines[] = '<hr style="border:0;border-top:1px solid rgba(255,255,255,.1);margin:8px 0">';
|
||
$lines[] = '📤 <b>Bilder hochladen – so geht\'s:</b>';
|
||
$lines[] = '① <b>Galerie öffnen</b> und auf <em>„Bilder hochladen"</em> klicken.';
|
||
$lines[] = '② Deinen <b>Minecraft-Namen</b> eingeben und den Server wählen → <em>Session starten</em>.';
|
||
$lines[] = '③ Im Spiel den Befehl <code>/verify [token]</code> eingeben, um dich zu verifizieren.';
|
||
$lines[] = '④ <b>Screenshots auswählen</b> (optional Album anlegen) → hochladen ✓';
|
||
$lines[] = '<small>💡 Der Verify-Token ist 5 Minuten gültig. Du musst dazu online auf dem Server sein.</small>';
|
||
} else {
|
||
// Kurz-Hinweis auf Upload auch bei allgemeiner Galerie-Frage
|
||
$lines[] = "🖼️ Du kannst eigene Screenshots direkt auf der Galerie-Seite hochladen.";
|
||
}
|
||
|
||
// ── 3. Bild des Tages ────────────────────────────────────
|
||
if ( post_type_exists( 'mc_gallery' ) ) {
|
||
$today_key = 'mc_daily_image_' . gmdate( 'Y-m-d' );
|
||
$image_id = get_transient( $today_key );
|
||
|
||
if ( $image_id && wp_attachment_is_image( $image_id ) ) {
|
||
$img = wp_get_attachment_image_src( $image_id, 'medium' );
|
||
$full = wp_get_attachment_image_src( $image_id, 'full' );
|
||
$meta = get_post( $image_id );
|
||
$uploader = '';
|
||
|
||
// Spielername aus Galerie-Post-Meta holen
|
||
if ( $meta && $meta->post_parent ) {
|
||
$player = get_post_meta( $meta->post_parent, 'mc_player', true );
|
||
if ( $player ) {
|
||
$uploader = ' <small>von <b>' . esc_html( $player ) . '</b></small>';
|
||
}
|
||
}
|
||
|
||
if ( $img ) {
|
||
$lines[] = '<hr style="border:0;border-top:1px solid rgba(255,255,255,.1);margin:8px 0">';
|
||
$lines[] = '🌟 <b>Bild des Tages' . $uploader . ':</b>';
|
||
$lines[] = "<a href='" . esc_url( $full ? $full[0] : $img[0] ) . "' target='_blank'>"
|
||
. "<img src='" . esc_url( $img[0] ) . "' "
|
||
. "style='max-width:100%;border-radius:8px;margin-top:4px;cursor:pointer;' "
|
||
. "alt='Bild des Tages'>"
|
||
. "</a>";
|
||
}
|
||
} else {
|
||
// Kein Tagesbild gecacht → Hinweis
|
||
$lines[] = '<small>📅 Heute noch kein Bild des Tages verfügbar.</small>';
|
||
}
|
||
|
||
// Galerie-Statistik
|
||
$count = wp_count_posts( 'mc_gallery' );
|
||
if ( $count && $count->publish > 0 ) {
|
||
$lines[] = '<small>📁 ' . intval( $count->publish ) . ' Galerie(n) auf dem Server.</small>';
|
||
}
|
||
}
|
||
|
||
return [
|
||
'source' => 'gallery',
|
||
'title' => $is_upload_query ? '📤 Galerie & Upload' : '📷 Galerie',
|
||
'content' => implode( '<br>', $lines ),
|
||
];
|
||
}
|
||
|
||
// ============================================================
|
||
// INTENT: FAQ (Custom Post Type 'faq')
|
||
// ============================================================
|
||
|
||
function mm_intent_faq( $q ) {
|
||
if ( ! post_type_exists( 'faq' ) ) return null;
|
||
|
||
$search = preg_replace( '/\b(faq|häufig|frage|oft gefragt)\b/i', '', $q );
|
||
$search = trim( $search );
|
||
|
||
$results = get_posts( [
|
||
'post_type' => 'faq',
|
||
'post_status' => 'publish',
|
||
's' => strlen( $search ) >= 3 ? $search : '',
|
||
'posts_per_page' => 4,
|
||
'orderby' => 'relevance',
|
||
] );
|
||
|
||
if ( empty( $results ) ) return null;
|
||
|
||
$html = [];
|
||
foreach ( $results as $post ) {
|
||
$excerpt = get_the_excerpt( $post->ID );
|
||
$html[] = '❓ <b>' . esc_html( $post->post_title ) . '</b>'
|
||
. ( $excerpt ? '<br>' . wp_trim_words( $excerpt, 40, '…' ) : '' );
|
||
}
|
||
|
||
return [
|
||
'source' => 'faq',
|
||
'title' => '❓ FAQ',
|
||
'content' => implode( '<br><br>', $html ),
|
||
];
|
||
}
|
||
|
||
// ============================================================
|
||
// SPIELER-NAME EXTRAHIEREN
|
||
// ============================================================
|
||
|
||
function mm_extract_player_name( $q ) {
|
||
// Explizit: "von Steve", "spieler Steve", "für Steve"
|
||
if ( preg_match( '/(?:von|spieler|name|für|player|ban.*?von|bann.*?von)[:\s]+([A-Za-z0-9_]{3,16})/iu', $q, $m ) ) {
|
||
return $m[1];
|
||
}
|
||
// "Steve ist gebannt", "ist Steve gebannt"
|
||
if ( preg_match( '/\b([A-Za-z0-9_]{3,16})\s+ist\s+(gebannt|gesperrt|online|offline)/i', $q, $m ) ) {
|
||
return $m[1];
|
||
}
|
||
// "bin ich" → kein Name (eingeloggter User, kein Minecraft-Login hier)
|
||
if ( preg_match( '/\b(bin ich|ich bin|mein ban|mein status)\b/i', $q ) ) {
|
||
return '';
|
||
}
|
||
// Allgemein: letztes kapitalisiertes Wort als Spielername (MC-Namen beginnen meist groß)
|
||
if ( preg_match_all( '/\b([A-Z][A-Za-z0-9_]{2,15})\b/', $q, $m ) ) {
|
||
$stop = [ 'Minecraft', 'Wiki', 'Forum', 'FAQ', 'Shop', 'Discord', 'Ban', 'Mute',
|
||
'Kick', 'Warn', 'Spielzeit', 'Playtime', 'Galerie', 'Ticket', 'Support' ];
|
||
foreach ( array_reverse( $m[1] ) as $candidate ) {
|
||
if ( ! in_array( $candidate, $stop, true ) ) {
|
||
return $candidate;
|
||
}
|
||
}
|
||
}
|
||
return '';
|
||
}
|
||
|
||
// ============================================================
|
||
// FALLBACK
|
||
// ============================================================
|
||
|
||
function mm_fallback_response( $bot ) {
|
||
$hints = [
|
||
'🖥️ <b>Server</b> – z.B. "Wie ist die Server-IP?"',
|
||
];
|
||
if ( ! empty( $bot['url_rules'] ) ) $hints[] = '📜 <b>Regeln</b> – z.B. "Ist PvP erlaubt?"';
|
||
if ( ! empty( $bot['url_player_history'] ) ) $hints[] = '👤 <b>Spieler-Info</b> – z.B. "Spielzeit von Steve"';
|
||
if ( ! empty( $bot['litebans_dashboard_url'] ) || ! empty( get_option( 'wp_litebans_pro_settings', [] )['db_name'] ) )
|
||
$hints[] = '🔨 <b>Ban-Status</b> – z.B. "Ist Steve gebannt?"';
|
||
if ( ! empty( $bot['url_wiki'] ) ) $hints[] = '📖 <b>Wiki</b> – z.B. "Wiki Befehle"';
|
||
if ( ! empty( $bot['url_gallery'] ) ) $hints[] = '📷 <b>Galerie</b> – z.B. "Zeig die Galerie"';
|
||
if ( ! empty( $bot['url_shop'] ) ) $hints[] = '🛒 <b>Shop</b> – z.B. "Was kostet ein Diamantschwert?"';
|
||
if ( ! empty( $bot['url_tickets'] ) ) $hints[] = '🎫 <b>Ticket</b> – z.B. "Ticket erstellen"';
|
||
|
||
return [
|
||
'source' => 'fallback',
|
||
'title' => '❓ Ich habe dazu keine Antwort',
|
||
'content' => 'Versuche es mit einem dieser Themen:<br>' . implode( '<br>', $hints ),
|
||
];
|
||
}
|
||
|
||
// ============================================================
|
||
// RESPONSE BUILDER
|
||
// Alle Part-Arrays zu einem einzigen formatierten HTML-String
|
||
// zusammenführen.
|
||
// ============================================================
|
||
|
||
function mm_build_response( $parts ) {
|
||
if ( count( $parts ) === 1 ) {
|
||
$p = $parts[0];
|
||
return [
|
||
'reply' => isset( $p['title'] )
|
||
? "<b>{$p['title']}</b><br>{$p['content']}"
|
||
: $p['content'],
|
||
'parts' => $parts,
|
||
];
|
||
}
|
||
|
||
$blocks = [];
|
||
foreach ( $parts as $p ) {
|
||
$block = isset( $p['title'] ) ? "<b>{$p['title']}</b><br>" : '';
|
||
$block .= $p['content'];
|
||
$blocks[] = $block;
|
||
}
|
||
|
||
return [
|
||
'reply' => implode( '<hr style="margin:10px 0;border:0;border-top:1px solid rgba(255,255,255,.15)">', $blocks ),
|
||
'parts' => $parts,
|
||
];
|
||
} |