{
  "name": "Translator",
  "author": "limon",
  "supportedGames": [
    "all"
  ],
  "takaroVersion": "v0.0.29",
  "versions": [
    {
      "tag": "1.2.2",
      "description": "Enables real-time translation of in-game chat messages, facilitating cross-lingual communication among players.\n\n  ## Key Functionality\n\n  * **Two-Way Translation:** Translates chat messages between players speaking different languages.\n  * **Automatic Translation:** Can automatically translate messages based on players' configured language preferences.\n  * **On-Demand Translation:** Players can translate specific messages or recent messages from other players using commands.\n  * **Configurable Languages:** Server administrators can define the list of supported languages.\n  * **Google Translate Integration:** Leverages the Google Cloud Translation API for accurate and comprehensive language support.\n  * **Permission Control:** Granular permissions allow administrators to control who can use translation features.\n\n  ## How to Use\n\n  1.  **Configuration:**\n      * `googleApiKey`: **(Required)** Enter your Google Cloud API key with access to the Cloud Translation API.\n      * `maxMessageLength`: Set the maximum character length for messages to be translated (to manage API usage/costs). Default: 500.\n      * `defaultLanguage`: Set the default language code (ISO 639-1) for translations when a player hasn't specified their preference. Default: `en`.\n      * `supportedLanguages`:  Define an array of ISO 639-1 language codes (e.g., `[\"en\", \"es\", \"fr\"]`) for the languages supported by the module.\n      * `commandPrefixes`: (Optional) An array of prefixes that are used to identify commands. Messages that start with these prefixes will not be translated. Default: `[\"/\"]`\n  2.  **Permissions:**\n      * `TRANSLATE_PERMISSION`:  Grants players access to basic translation commands (`/translate`, `/setlanguage`, `/toggletranslation`, `/translateme`).\n      * `TRANSLATE_ADMIN`:  Allows administrators to set language preferences for other players (`/setplayerlanguage`).\n      * `AUTO_TRANSLATE_PERMISSION`: Allows players to receive automatically translated messages.\n  3.  **In-Game Usage:**\n      * `/translate <language> \"text\"`: Translates the provided `text` into the specified `language` and sends it to the global chat.\n      * `/setlanguage <code>`: Sets the player's preferred language for receiving translations (e.g., `/setlanguage es`).\n      * `/toggletranslation on/off`: Enables or disables automatic translation for the player.\n      * `/translateme <player> [count]`: Translates the last `count` messages from the specified `player` and sends them to the user via DM. If `count` is omitted, translates the last message.\n      * `/setplayerlanguage <player> <language>`: (Admin only) Sets the preferred language for another player.\n\n  ## Important Considerations\n\n  * **Google Cloud API Key:** A valid Google Cloud API key with the Cloud Translation API enabled is **essential** for this module to function.\n  * **Language Codes:** Use correct ISO 639-1 two-letter language codes (e.g., 'en', 'es', 'fr'). Refer to the Google Cloud Translation API documentation for a full list.\n  * **Permissions Management:** Carefully manage the module's permissions to control access to translation features.\n  * **Message Length:** Be mindful of the `maxMessageLength` setting to avoid excessive API usage and potential costs.\n  * **Error Handling:** The module includes error handling, but it's important to monitor server logs for any translation-related issues.\n",
      "configSchema": "{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"required\":[\"googleApiKey\"],\"additionalProperties\":false,\"properties\":{\"googleApiKey\":{\"title\":\"googleApiKey\",\"description\":\"Your Google Cloud API key with access to the Translation API\",\"type\":\"string\",\"minLength\":1},\"maxMessageLength\":{\"title\":\"maxMessageLength\",\"description\":\"Maximum length of messages that will be translated (to control API costs)\",\"default\":500,\"type\":\"number\",\"minimum\":1},\"enabledByDefault\":{\"title\":\"enabledByDefault\",\"description\":\"If true, all new players will automatically receive translated messages in English\",\"default\":false,\"type\":\"boolean\"},\"defaultLanguage\":{\"title\":\"defaultLanguage\",\"description\":\"The default language code to use if enabledByDefault is true\",\"default\":\"en\",\"type\":\"string\"},\"supportedLanguages\":{\"title\":\"supportedLanguages\",\"description\":\"List of supported language codes for translation\",\"default\":[\"en\",\"es\",\"fr\",\"de\",\"it\",\"pt\",\"ru\",\"zh\",\"ja\",\"ko\",\"ar\",\"hi\",\"zh-CN\",\"ro\"],\"type\":\"array\",\"items\":{\"type\":\"string\"}}}}",
      "uiSchema": "{}",
      "commands": [
        {
          "name": "gettranslation",
          "trigger": "translateme",
          "helpText": "No help text available",
          "function": "import { takaro, data, TakaroUserError } from '@takaro/helpers';\n\nconst LANGUAGE_VARIABLE_KEY = 'user_language';\n\nasync function translateText(text, targetLanguage, apiKey) {\n    if (!text || !text.trim()) return '';\n\n    try {\n        const response = await takaro.axios.post(\n            `https://translation.googleapis.com/language/translate/v2?key=${apiKey}`,\n            { q: text, target: targetLanguage },\n            { headers: { 'Content-Type': 'application/json' } }\n        );\n\n        const result = response.data;\n\n        if (result.error) {\n            throw new Error(result.error.message || \"Unknown translation error\");\n        }\n\n        if (!result.data?.translations?.[0]) {\n            throw new Error(\"Invalid response format from translation API\");\n        }\n\n        return result.data.translations[0].translatedText;\n    } catch (error) {\n        console.error(`Translation API error:`, error);\n        return '[Translation error]';\n    }\n}\n\nasync function main() {\n    try {\n        const { player, arguments: args, gameServerId, module: mod } = data;\n\n        // Validate configuration\n        if (!mod.userConfig.googleApiKey) {\n            throw new TakaroUserError('Translation feature is not configured. Google API key is missing.');\n        }\n\n        // Find target player\n        if (!args.targetPlayerName) {\n            throw new TakaroUserError('Please specify a player to translate messages from.');\n        }\n\n        const targetPlayer = await getTargetPlayer(args.targetPlayerName);\n\n        // Determine target language\n        const targetLanguage = await determineTargetLanguage(args.targetLang, player.id, gameServerId, mod);\n\n        // Get message count (between 1-20)\n        const messageCount = Math.min(Math.max(parseInt(args.count || 5, 10), 1), 20);\n\n        // Fetch and translate messages\n        const messages = await fetchRecentMessages(targetPlayer.id, gameServerId, messageCount);\n\n        const consolidatedMessage = await buildTranslatedOutput(messages, targetPlayer.name, targetLanguage, mod.userConfig.googleApiKey);\n\n        await player.pm(consolidatedMessage);\n\n    } catch (error) {\n        console.error('Error in translation module:', error);\n        throw error instanceof TakaroUserError\n            ? error\n            : new TakaroUserError(`An error occurred while translating messages: ${error.message}`);\n    }\n}\n\nasync function getTargetPlayer(targetPlayerName) {\n    if (targetPlayerName.id) return targetPlayerName;\n\n    const playerSearch = await takaro.player.playerControllerSearch({\n        search: { name: [targetPlayerName] }\n    });\n\n    if (!playerSearch.data.data?.length) {\n        throw new TakaroUserError(`Player \"${targetPlayerName}\" not found.`);\n    }\n\n    return playerSearch.data.data[0];\n}\n\nasync function determineTargetLanguage(requestedLanguage, playerId, gameServerId, mod) {\n    // First priority: explicitly requested language\n    if (requestedLanguage) {\n        validateLanguage(requestedLanguage, mod);\n        return requestedLanguage;\n    }\n\n    // Second priority: user's saved preference\n    const userLanguageVar = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: [LANGUAGE_VARIABLE_KEY],\n            gameServerId: [gameServerId],\n            playerId: [playerId],\n            moduleId: [mod.moduleId]\n        }\n    });\n\n    if (userLanguageVar.data.data?.length) {\n        const savedLanguage = userLanguageVar.data.data[0].value;\n        validateLanguage(savedLanguage, mod);\n        return savedLanguage;\n    }\n\n    // Last priority: module default or English\n    const defaultLanguage = mod.userConfig.defaultLanguage || 'en';\n    validateLanguage(defaultLanguage, mod);\n    return defaultLanguage;\n}\n\nfunction validateLanguage(language, mod) {\n    if (mod.userConfig.supportedLanguages &&\n        !mod.userConfig.supportedLanguages.includes(language)) {\n        throw new TakaroUserError(\n            `Unsupported target language: ${language}. ` +\n            `Available languages: ${mod.userConfig.supportedLanguages.join(', ')}`\n        );\n    }\n}\n\nasync function fetchRecentMessages(playerId, gameServerId, messageCount) {\n    const events = await takaro.event.eventControllerSearch({\n        limit: 100,\n        sortBy: 'createdAt',\n        sortDirection: 'desc',\n        filters: {\n            eventName: ['chat-message'],\n            gameserverId: [gameServerId],\n            playerId: [playerId]\n        }\n    });\n\n    if (!events.data.data?.length) {\n        throw new TakaroUserError(`No chat messages found for this player.`);\n    }\n\n    // Filter out command messages (messages starting with '/' or '!')\n    const filteredMessages = events.data.data\n        .filter(event => {\n            const message = event.meta.msg;\n            return message && typeof message === 'string' &&\n                !message.startsWith('/') &&\n                !message.startsWith('!');\n        })\n        .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))\n        .slice(0, messageCount)\n        .map(event => ({\n            content: event.meta.msg,\n            timestamp: event.createdAt\n        }));\n\n    if (filteredMessages.length === 0) {\n        throw new TakaroUserError(`No non-command messages found for this player.`);\n    }\n\n    return filteredMessages;\n}\n\nasync function buildTranslatedOutput(messages, playerName, targetLanguage, apiKey) {\n    let output = `Messages from ${playerName} (${targetLanguage}):\\n\\n`;\n\n    // Reverse the messages array to start from the oldest\n    const reversedMessages = [...messages].reverse();\n\n    for (let i = 0; i < reversedMessages.length; i++) {\n        const message = reversedMessages[i];\n        const translatedText = await translateText(message.content, targetLanguage, apiKey);\n\n        // Just show the translation, not the original message\n        output += `${i + 1}. ${translatedText}\\n\\n`;\n    }\n\n    return output;\n}\n\nawait main();\n",
          "arguments": [
            {
              "name": "targetPlayerName",
              "type": "string",
              "helpText": "player name",
              "defaultValue": "",
              "position": 0
            }
          ]
        },
        {
          "name": "translateme",
          "trigger": "translateme",
          "helpText": "Translate recent messages from a specific player into your preferred language. Specify a number to translate multiple messages.",
          "function": "import { takaro, data, TakaroUserError, checkPermission } from '@takaro/helpers';\n\nconst LANGUAGE_VARIABLE_KEY = 'user_language';\nconst TRANSLATE_PERMISSION = 'TRANSLATE_PERMISSION';\n\nasync function translateText(text, targetLanguage, apiKey) {\n    if (!text || !text.trim()) return '';\n\n    try {\n        const response = await takaro.axios.post(\n            `https://translation.googleapis.com/language/translate/v2?key=${apiKey}`,\n            { q: text, target: targetLanguage },\n            { headers: { 'Content-Type': 'application/json' } }\n        );\n\n        const result = response.data;\n\n        if (result.error) {\n            throw new Error(result.error.message || \"Unknown translation error\");\n        }\n\n        if (!result.data?.translations?.[0]) {\n            throw new Error(\"Invalid response format from translation API\");\n        }\n\n        return result.data.translations[0].translatedText;\n    } catch (error) {\n        console.error(`Translation API error:`, error);\n        return '[Translation error]';\n    }\n}\n\nasync function main() {\n    try {\n        const { player, arguments: args, gameServerId, module: mod, pog } = data;\n\n        // Check permission - ensure player has TRANSLATE_PERMISSION\n        if (!checkPermission(pog, TRANSLATE_PERMISSION)) {\n            throw new TakaroUserError(\"You don't have permission to use the translation feature.\");\n        }\n\n        // Validate configuration\n        if (!mod.userConfig.googleApiKey) {\n            throw new TakaroUserError('Translation feature is not configured. Google API key is missing.');\n        }\n\n        // Find target player\n        if (!args.targetPlayerName) {\n            throw new TakaroUserError('Please specify a player to translate messages from.');\n        }\n\n        const targetPlayer = await getTargetPlayer(args.targetPlayerName);\n\n        // Determine target language\n        const targetLanguage = await determineTargetLanguage(args.targetLang, player.id, gameServerId, mod);\n\n        // Get message count from args or default to 1 (just the last message)\n        const messageCount = args.count ? Math.min(Math.max(parseInt(args.count, 10), 1), 20) : 1;\n\n        // Fetch and translate messages\n        const messages = await fetchRecentMessages(targetPlayer.id, gameServerId, messageCount);\n\n        const consolidatedMessage = await buildTranslatedOutput(messages, targetPlayer.name, targetLanguage, mod.userConfig.googleApiKey);\n\n        await player.pm(consolidatedMessage);\n\n    } catch (error) {\n        console.error('Error in translation module:', error);\n        throw error instanceof TakaroUserError\n            ? error\n            : new TakaroUserError(`An error occurred while translating messages: ${error.message}`);\n    }\n}\n\nasync function getTargetPlayer(targetPlayerName) {\n    if (targetPlayerName.id) return targetPlayerName;\n\n    const playerSearch = await takaro.player.playerControllerSearch({\n        search: { name: [targetPlayerName] }\n    });\n\n    if (!playerSearch.data.data?.length) {\n        throw new TakaroUserError(`Player \"${targetPlayerName}\" not found.`);\n    }\n\n    return playerSearch.data.data[0];\n}\n\nasync function determineTargetLanguage(requestedLanguage, playerId, gameServerId, mod) {\n    // First priority: explicitly requested language\n    if (requestedLanguage) {\n        validateLanguage(requestedLanguage, mod);\n        return requestedLanguage;\n    }\n\n    // Second priority: user's saved preference\n    const userLanguageVar = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: [LANGUAGE_VARIABLE_KEY],\n            gameServerId: [gameServerId],\n            playerId: [playerId],\n            moduleId: [mod.moduleId]\n        }\n    });\n\n    if (userLanguageVar.data.data?.length) {\n        const savedLanguage = userLanguageVar.data.data[0].value;\n        validateLanguage(savedLanguage, mod);\n        return savedLanguage;\n    }\n\n    // Last priority: module default or English\n    const defaultLanguage = mod.userConfig.defaultLanguage || 'en';\n    validateLanguage(defaultLanguage, mod);\n    return defaultLanguage;\n}\n\nfunction validateLanguage(language, mod) {\n    if (mod.userConfig.supportedLanguages &&\n        !mod.userConfig.supportedLanguages.includes(language)) {\n        throw new TakaroUserError(\n            `Unsupported target language: ${language}. ` +\n            `Available languages: ${mod.userConfig.supportedLanguages.join(', ')}`\n        );\n    }\n}\n\nasync function fetchRecentMessages(playerId, gameServerId, messageCount) {\n    const events = await takaro.event.eventControllerSearch({\n        limit: 100,\n        sortBy: 'createdAt',\n        sortDirection: 'desc',\n        filters: {\n            eventName: ['chat-message'],\n            gameserverId: [gameServerId],\n            playerId: [playerId]\n        }\n    });\n\n    if (!events.data.data?.length) {\n        throw new TakaroUserError(`No chat messages found for this player.`);\n    }\n\n    // Filter out command messages (messages starting with '/' or '!')\n    const filteredMessages = events.data.data\n        .filter(event => {\n            const message = event.meta.msg;\n            return message && typeof message === 'string' &&\n                !message.startsWith('/') &&\n                !message.startsWith('!');\n        })\n        .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))\n        .slice(0, messageCount)\n        .map(event => ({\n            content: event.meta.msg,\n            timestamp: event.createdAt\n        }));\n\n    if (filteredMessages.length === 0) {\n        throw new TakaroUserError(`No non-command messages found for this player.`);\n    }\n\n    return filteredMessages;\n}\n\nasync function buildTranslatedOutput(messages, playerName, targetLanguage, apiKey) {\n    let output = `Messages from ${playerName} (${targetLanguage}):\\n\\n`;\n\n    // Reverse the messages array to start from the oldest\n    const reversedMessages = [...messages].reverse();\n\n    for (let i = 0; i < reversedMessages.length; i++) {\n        const message = reversedMessages[i];\n        const translatedText = await translateText(message.content, targetLanguage, apiKey);\n\n        // Just show the translation, not the original message\n        output += `${i + 1}. ${translatedText}\\n\\n`;\n    }\n\n    return output;\n}\n\nawait main();",
          "arguments": [
            {
              "name": "targetPlayerName",
              "type": "string",
              "helpText": "player name",
              "defaultValue": "",
              "position": 0
            },
            {
              "name": "count",
              "type": "string",
              "helpText": "Number of most recent messages to translate (default: 1)",
              "defaultValue": "1",
              "position": 1
            }
          ]
        },
        {
          "name": "setplayerlanguage",
          "trigger": "setplayerlanguage",
          "helpText": "No help text available",
          "function": "import { takaro, data, TakaroUserError, checkPermission } from '@takaro/helpers';\n\nconst LANGUAGE_VARIABLE_KEY = 'user_language';\n\nasync function main() {\n    const { player, pog, gameServerId, module, arguments: args } = data;\n\n    // Check if player has admin permissions to set other players' languages\n    if (!checkPermission(pog, 'TRANSLATE_ADMIN')) {\n        throw new TakaroUserError('You do not have permission to set languages for other players.');\n    }\n\n    // Validate target player parameter\n    if (!args.targetPlayer) {\n        throw new TakaroUserError('Please specify a player name. Usage: /setplayerlanguage <player> <language>');\n    }\n\n    // Validate language parameter\n    if (!args.language) {\n        throw new TakaroUserError('Please specify a language code. Usage: /setplayerlanguage <player> <language>');\n    }\n\n    // Find the target player\n    let targetPlayer;\n    try {\n        const targetPlayerSearch = await takaro.player.playerControllerSearch({\n            search: { name: [args.targetPlayer] }\n        });\n\n        if (!targetPlayerSearch.data.data?.length) {\n            throw new TakaroUserError(`Player \"${args.targetPlayer}\" not found.`);\n        }\n\n        targetPlayer = targetPlayerSearch.data.data[0];\n    } catch (error) {\n        throw new TakaroUserError(`Failed to find player: ${error.message}`);\n    }\n\n    // Validate language code against supported languages in config\n    const supportedLanguages = module.userConfig.supportedLanguages || [];\n    if (supportedLanguages.length > 0 && !supportedLanguages.includes(args.language)) {\n        throw new TakaroUserError(\n            `Invalid language code. Valid options are: ${supportedLanguages.join(', ')}`\n        );\n    }\n\n    // Convert to lowercase for consistency\n    const language = args.language.toLowerCase();\n\n    try {\n        // Check if target player already has a language set\n        const existingLanguageVar = await takaro.variable.variableControllerSearch({\n            filters: {\n                key: [LANGUAGE_VARIABLE_KEY],\n                gameServerId: [gameServerId],\n                playerId: [targetPlayer.id],\n                moduleId: [module.moduleId]\n            }\n        });\n\n        // If language variable exists, update it\n        if (existingLanguageVar.data.data.length > 0) {\n            await takaro.variable.variableControllerUpdate(existingLanguageVar.data.data[0].id, {\n                value: language\n            });\n        }\n        // Otherwise create a new variable\n        else {\n            await takaro.variable.variableControllerCreate({\n                key: LANGUAGE_VARIABLE_KEY,\n                value: language,\n                gameServerId: gameServerId,\n                playerId: targetPlayer.id,\n                moduleId: module.moduleId\n            });\n        }\n\n        await player.pm(`Language for ${targetPlayer.name} has been set to: ${language}`);\n\n        // Also notify the target player if they're online\n        try {\n            const pogList = await takaro.playerOnGameserver.playerOnGameServerControllerSearch({\n                filters: {\n                    gameServerId: [gameServerId],\n                    playerId: [targetPlayer.id],\n                    online: [true]\n                }\n            });\n\n            if (pogList.data.data.length > 0) {\n                // The player is online, we can send them a notification\n                await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n                    message: `An admin has set your translation language to: ${language}`,\n                    opts: {\n                        recipient: {\n                            gameId: pogList.data.data[0].gameId\n                        }\n                    }\n                });\n            }\n        } catch (error) {\n            console.error(`Error notifying target player: ${error.message}`);\n            // We don't want to fail the whole command if just the notification fails\n        }\n\n    } catch (error) {\n        console.error(`Error setting player language: ${error.message}`);\n        throw new TakaroUserError(`Failed to set language: ${error.message}`);\n    }\n}\n\nawait main();",
          "arguments": [
            {
              "name": "targetPlayer",
              "type": "string",
              "helpText": "Name of the player to set language for",
              "defaultValue": "",
              "position": 0
            },
            {
              "name": "language",
              "type": "string",
              "helpText": "Language code to set (e.g., 'en' for English)",
              "defaultValue": "",
              "position": 1
            }
          ]
        },
        {
          "name": "toggletranslation",
          "trigger": "toggletranslation",
          "helpText": "Enable or disable automatic translation of messages. Use 'toggletranslation on' to receive translated messages, 'toggletranslation off' to disable.",
          "function": "import { takaro, data, TakaroUserError, checkPermission } from '@takaro/helpers';\n\nconst TRANSLATION_ENABLED_KEY = 'translation_enabled';\n\nasync function main() {\n    const { player, pog, gameServerId, module, arguments: args } = data;\n\n    // Check if player has permission to use this command\n    if (!checkPermission(pog, 'TRANSLATE_PERMISSION')) {\n        throw new TakaroUserError('You do not have permission to use translation features.');\n    }\n\n    // Get the current status if no argument is provided\n    if (!args.status || (args.status !== 'on' && args.status !== 'off')) {\n        const currentStatusVar = await takaro.variable.variableControllerSearch({\n            filters: {\n                key: [TRANSLATION_ENABLED_KEY],\n                gameServerId: [gameServerId],\n                playerId: [player.id],\n                moduleId: [module.moduleId]\n            }\n        });\n\n        // Check if we have a stored preference\n        if (currentStatusVar.data.data.length > 0) {\n            const isEnabled = currentStatusVar.data.data[0].value === 'true';\n            await player.pm(`Your translation status is currently: ${isEnabled ? 'ENABLED' : 'DISABLED'}`);\n            await player.pm('Use \"/toggletranslation on\" or \"/toggletranslation off\" to change this setting.');\n        } else {\n            // If no preference is set, check the module default setting\n            const defaultEnabled = module.userConfig.enabledByDefault ?? false;\n            await player.pm(`Your translation status is currently: ${defaultEnabled ? 'ENABLED' : 'DISABLED'} (using default setting)`);\n            await player.pm('Use \"/toggletranslation on\" or \"/toggletranslation off\" to change this setting.');\n        }\n        return;\n    }\n\n    // Convert argument to boolean\n    const newStatus = args.status === 'on';\n\n    // Check if user already has a preference set\n    const existingStatusVar = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: [TRANSLATION_ENABLED_KEY],\n            gameServerId: [gameServerId],\n            playerId: [player.id],\n            moduleId: [module.moduleId]\n        }\n    });\n\n    // If preference exists, update it\n    if (existingStatusVar.data.data.length > 0) {\n        await takaro.variable.variableControllerUpdate(existingStatusVar.data.data[0].id, {\n            value: newStatus.toString()\n        });\n    }\n    // Otherwise create a new variable\n    else {\n        await takaro.variable.variableControllerCreate({\n            key: TRANSLATION_ENABLED_KEY,\n            value: newStatus.toString(),\n            gameServerId: gameServerId,\n            playerId: player.id,\n            moduleId: module.moduleId\n        });\n    }\n\n    await player.pm(`Translation has been ${newStatus ? 'ENABLED' : 'DISABLED'} for you.`);\n\n    // If translations were enabled but no language is set, prompt the user\n    if (newStatus) {\n        const languageVar = await takaro.variable.variableControllerSearch({\n            filters: {\n                key: ['user_language'],\n                gameServerId: [gameServerId],\n                playerId: [player.id],\n                moduleId: [module.moduleId]\n            }\n        });\n\n        if (languageVar.data.data.length === 0) {\n            const defaultLanguage = module.userConfig.defaultLanguage || 'en';\n            await player.pm(`No language preference is set. Messages will be translated to ${defaultLanguage} by default.`);\n            await player.pm(`Use \"/setlanguage <code>\" to set your preferred language.`);\n            await player.pm(`Example: /setlanguage es (for Spanish)`);\n\n            // List available languages\n            if (module.userConfig.supportedLanguages && module.userConfig.supportedLanguages.length > 0) {\n                await player.pm(`Supported languages: ${module.userConfig.supportedLanguages.join(', ')}`);\n            }\n        }\n    }\n}\n\nawait main();",
          "arguments": [
            {
              "name": "status",
              "type": "string",
              "helpText": "on/off to enable/disable translations",
              "defaultValue": "off",
              "position": 0
            }
          ]
        },
        {
          "name": "translate",
          "trigger": "translate",
          "helpText": "No help text available",
          "function": "import { takaro, data, TakaroUserError } from '@takaro/helpers';\n\nasync function main() {\n    const { player, gameServerId, module: mod, arguments: args } = data;\n\n    // Get the text to translate\n    const textToTranslate = args.text;\n\n    if (!textToTranslate || textToTranslate.trim() === \"\") {\n        throw new TakaroUserError(\"No text to translate was provided.\");\n    }\n\n    // Validate language code against supported languages in config\n    const validLanguages = mod.userConfig.supportedLanguages;\n    if (!validLanguages.includes(args.language)) {\n        throw new TakaroUserError(\n            `Invalid language code. Valid options are: ${validLanguages.join(', ')}`\n        );\n    }\n\n    // First verify API key is configured\n    if (!mod.userConfig.googleApiKey) {\n        throw new TakaroUserError('Translation service is not properly configured. Please contact an administrator.');\n    }\n\n    try {\n        // Translate the text\n        const apiKey = mod.userConfig.googleApiKey;\n        const translation = await translateText(textToTranslate, args.language, apiKey);\n\n        // Get language name for better UX\n        const languageNames = {\n            'en': 'English',\n            'es': 'Spanish',\n            'fr': 'French',\n            'de': 'German',\n            'it': 'Italian',\n            'pt': 'Portuguese',\n            'ru': 'Russian',\n            'zh': 'Chinese',\n            'ja': 'Japanese',\n            'ko': 'Korean',\n            'ar': 'Arabic',\n            'hi': 'Hindi',\n            'tr': 'Turkish',\n            'nl': 'Dutch',\n            'pl': 'Polish',\n            'vi': 'Vietnamese',\n            'th': 'Thai',\n            'id': 'Indonesian',\n            'zh-CN': 'Chinese Simplified',\n            'ro': 'Romanian',\n            'pl': 'Polish'\n        };\n        const languageName = languageNames[args.language] || args.language;\n\n        // Send translation to the game server global chat\n        await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n            message: `[${player.name} -> ${languageName}]: ${translation}`\n        });\n\n    } catch (error) {\n        console.error(`Translation error: ${error.message}`);\n        throw new TakaroUserError(`Translation failed: ${error.message}`);\n    }\n}\n\n// Translation function using Takaro's HTTP methods\nasync function translateText(text, targetLanguage, apiKey) {\n    try {\n        // Use axios (available in the Takaro environment)\n        const response = await takaro.axios.post(\n            `https://translation.googleapis.com/language/translate/v2?key=${apiKey}`,\n            {\n                q: text,\n                target: targetLanguage\n            },\n            { headers: { 'Content-Type': 'application/json' } }\n        );\n\n        const result = response.data;\n\n        if (result.error) {\n            throw new Error(result.error.message || \"Unknown translation error\");\n        }\n\n        if (!result.data || !result.data.translations || !result.data.translations[0]) {\n            throw new Error(\"Invalid response format from translation API\");\n        }\n\n        return result.data.translations[0].translatedText;\n    } catch (error) {\n        console.error(`Translation API error: ${error.message}`);\n        throw new Error(`API error: ${error.message}`);\n    }\n}\n\nawait main();",
          "arguments": [
            {
              "name": "language",
              "type": "string",
              "helpText": "what language you want to translate to?",
              "defaultValue": "",
              "position": 0
            },
            {
              "name": "text",
              "type": "string",
              "helpText": "The text that you want to translate",
              "defaultValue": "",
              "position": 1
            }
          ]
        },
        {
          "name": "setlanguage",
          "trigger": "setlanguage",
          "helpText": "No help text available",
          "function": "import { takaro, data, TakaroUserError } from '@takaro/helpers';\n\nconst LANGUAGE_VARIABLE_KEY = 'user_language';\n\nasync function main() {\n    const { player, pog, gameServerId, module, arguments: args } = data;\n\n    // List of supported languages - you can expand this as needed\n    const supportedLanguages = [\n        'en', 'es', 'fr', 'de', 'it', 'pt', 'ru', 'zh', 'ja', 'ko',\n        'ar', 'hi', 'bn', 'pa', 'te', 'mr', 'tr', 'vi', 'id', 'ms',\n        'th', 'nl', 'sv', 'no', 'da', 'fi', 'pl', 'uk', 'cs', 'sk',\n        'hu', 'ro', 'bg', 'el', 'he', 'fa', 'ro', 'zh-CN', 'zh-cn'\n    ];\n\n    // If no language specified, show current language or help\n    if (!args.language) {\n        const currentLangVar = await takaro.variable.variableControllerSearch({\n            filters: {\n                key: [LANGUAGE_VARIABLE_KEY],\n                gameServerId: [gameServerId],\n                playerId: [player.id],\n                moduleId: [module.moduleId]\n            }\n        });\n\n        if (currentLangVar.data.data.length > 0) {\n            const currentLang = currentLangVar.data.data[0].value;\n            await player.pm(`Your language is currently set to: ${currentLang}`);\n        } else {\n            await player.pm(`You haven't set a language yet. Use this command with a language code to set your language.`);\n            await player.pm(`Example: /setlanguage en`);\n            await player.pm(`Supported languages: ${supportedLanguages.join(', ')}`);\n        }\n        return;\n    }\n\n    // Convert to lowercase for consistency\n    const language = args.language.toLowerCase();\n\n    // Validate the language\n    if (!supportedLanguages.includes(language)) {\n        throw new TakaroUserError(`Unsupported language: ${language}. Supported languages: ${supportedLanguages.join(', ')}`);\n    }\n\n    // Check if user already has a language set\n    const existingLanguageVar = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: [LANGUAGE_VARIABLE_KEY],\n            gameServerId: [gameServerId],\n            playerId: [player.id],\n            moduleId: [module.moduleId]\n        }\n    });\n\n    // If language variable exists, update it\n    if (existingLanguageVar.data.data.length > 0) {\n        await takaro.variable.variableControllerUpdate(existingLanguageVar.data.data[0].id, {\n            value: language\n        });\n    }\n    // Otherwise create a new variable\n    else {\n        await takaro.variable.variableControllerCreate({\n            key: LANGUAGE_VARIABLE_KEY,\n            value: language,\n            gameServerId: gameServerId,\n            playerId: player.id,\n            moduleId: module.moduleId\n        });\n    }\n\n    await player.pm(`Your language has been set to: ${language}`);\n}\n\nawait main();",
          "arguments": [
            {
              "name": "language",
              "type": "string",
              "helpText": "Set your language",
              "defaultValue": "",
              "position": 0
            }
          ]
        }
      ],
      "hooks": [
        {
          "name": "transslator deactivate",
          "eventType": "log",
          "description": "Hook for log events",
          "regex": "takaro-hook-regex-placeholder",
          "function": "import { takaro, data } from '@takaro/helpers';\n\nconst LANGUAGE_VARIABLE_KEY = 'user_language';\nconst TRANSLATION_ENABLED_KEY = 'translation_enabled';\nconst TRANSLATOR_ACTIVE_STATE_KEY = 'translator_active_state'; // Stores 'true' or 'false'\n\nasync function checkAndSetTranslatorActiveState(gameServerId, mod, eventContext) {\n    takaro.log.info(`[Translator State] ${eventContext}: Attempting to update active translation state...`);\n\n    let rawOnlinePlayers;\n    try {\n        rawOnlinePlayers = (await takaro.gameserver.gameServerControllerGetPlayers(gameServerId)).data.data;\n    } catch (error) {\n        takaro.log.error(`[Translator State] ${eventContext}: Error fetching online players: ${error.message}. Cannot update state.`);\n        return;\n    }\n\n    // If the player who disconnected was the last one, rawOnlinePlayers might now be empty.\n    if (!rawOnlinePlayers || rawOnlinePlayers.length === 0) {\n        takaro.log.info(`[Translator State] ${eventContext}: No players reported online after event. Setting active translation state to false.`);\n        await updateTranslatorActiveState(gameServerId, mod.moduleId, false, eventContext);\n        return;\n    }\n\n    const onlinePlayerTakaroDetails = [];\n    for (const rawP of rawOnlinePlayers) {\n        const linkingId = rawP.steamId || rawP.platformId || rawP.id; // Adjust based on game server data\n        if (!linkingId) continue;\n\n        try {\n            let filter = {};\n            // Define filter based on available ID, e.g., steamId, platformId\n            if (rawP.steamId) filter = { steamId: [rawP.steamId.toString()] };\n            else if (rawP.platformId) filter = { platformId: [rawP.platformId.toString()] }; // Example\n            else if (rawP.id && typeof rawP.id === 'string' && rawP.id.length > 10) filter = { steamId: [rawP.id.toString()] }; // Fallback\n            else continue;\n\n            const playerRes = await takaro.player.playerControllerSearch({ filters: filter });\n            if (playerRes.data.data.length > 0) {\n                onlinePlayerTakaroDetails.push(playerRes.data.data[0]);\n            }\n        } catch (e) {\n            takaro.log.warn(`[Translator State] ${eventContext}: Error searching for Takaro player with linking ID ${linkingId}: ${e.message}`);\n        }\n    }\n\n    // It's possible that after a disconnect, no *Takaro-resolved* players remain, even if rawOnlinePlayers had entries.\n    if (onlinePlayerTakaroDetails.length < 1) {\n        takaro.log.info(`[Translator State] ${eventContext}: Fewer than 1 resolved Takaro players online after event. Setting active translation state to false.`);\n        await updateTranslatorActiveState(gameServerId, mod.moduleId, false, eventContext);\n        return;\n    }\n\n    const defaultLang = (mod.userConfig.defaultLanguage || 'en').toLowerCase();\n    const uniqueEnabledLanguages = new Set();\n    let translationPotentiallyNeeded = false;\n\n    for (const p of onlinePlayerTakaroDetails) {\n        try {\n            const enabledVar = await takaro.variable.variableControllerSearch({\n                filters: { key: [TRANSLATION_ENABLED_KEY], gameServerId, playerId: [p.id], moduleId: [mod.moduleId] }\n            });\n            let isPlayerEnabled = mod.userConfig.enabledByDefault ?? false;\n            if (enabledVar.data.data.length > 0 && enabledVar.data.data[0].value) {\n                isPlayerEnabled = enabledVar.data.data[0].value === 'true';\n            }\n\n            if (!isPlayerEnabled) {\n                continue;\n            }\n\n            const langVar = await takaro.variable.variableControllerSearch({\n                filters: { key: [LANGUAGE_VARIABLE_KEY], gameServerId, playerId: [p.id], moduleId: [mod.moduleId] }\n            });\n            let playerLang = defaultLang;\n            if (langVar.data.data.length > 0 && langVar.data.data[0].value) {\n                playerLang = langVar.data.data[0].value.toLowerCase();\n            }\n            uniqueEnabledLanguages.add(playerLang);\n\n        } catch (e) {\n            takaro.log.warn(`[Translator State] ${eventContext}: Error fetching language/enabled status for player ${p.name} (ID ${p.id}): ${e.message}.`);\n            uniqueEnabledLanguages.add(defaultLang);\n        }\n    }\n\n    if (uniqueEnabledLanguages.size === 0 && onlinePlayerTakaroDetails.length > 0) {\n        takaro.log.info(`[Translator State] ${eventContext}: All remaining online players have translation disabled. Setting active state to false.`);\n        translationPotentiallyNeeded = false;\n    } else if (uniqueEnabledLanguages.size > 1) {\n        takaro.log.info(`[Translator State] ${eventContext}: Multiple (${uniqueEnabledLanguages.size}) unique languages among remaining enabled players. Setting active state to true.`);\n        translationPotentiallyNeeded = true;\n    } else if (uniqueEnabledLanguages.size === 1) {\n        const singleLang = [...uniqueEnabledLanguages][0];\n        if (singleLang !== defaultLang) {\n            takaro.log.info(`[Translator State] ${eventContext}: Single unique language (\"${singleLang}\") among remaining enabled players, different from default (\"${defaultLang}\"). Setting active state to true.`);\n            translationPotentiallyNeeded = true;\n        } else {\n            takaro.log.info(`[Translator State] ${eventContext}: All remaining enabled online players use the default language (\"${defaultLang}\"). Setting active state to false.`);\n            translationPotentiallyNeeded = false;\n        }\n    } else { // No unique enabled languages found (e.g. if the only player with a different lang disconnected and no one else is enabled)\n        takaro.log.info(`[Translator State] ${eventContext}: No players with translation enabled or no unique languages identified among remaining players. Setting active state to false.`);\n        translationPotentiallyNeeded = false;\n    }\n\n    await updateTranslatorActiveState(gameServerId, mod.moduleId, translationPotentiallyNeeded, eventContext);\n}\n\nasync function updateTranslatorActiveState(gameServerId, moduleId, isActive, eventContext) {\n    try {\n        const existingStateVar = await takaro.variable.variableControllerSearch({\n            filters: { key: [TRANSLATOR_ACTIVE_STATE_KEY], gameServerId, moduleId }\n        });\n\n        if (existingStateVar.data.data.length > 0) {\n            const variableId = existingStateVar.data.data[0].id;\n            if (existingStateVar.data.data[0].value !== isActive.toString()) {\n                await takaro.variable.variableControllerUpdate(variableId, { value: isActive.toString() });\n                takaro.log.info(`[Translator State] ${eventContext}: Active translation state UPDATED to: ${isActive}`);\n            } else {\n                takaro.log.info(`[Translator State] ${eventContext}: Active translation state is already: ${isActive}. No update needed.`);\n            }\n        } else {\n            await takaro.variable.variableControllerCreate({\n                key: TRANSLATOR_ACTIVE_STATE_KEY,\n                value: isActive.toString(),\n                gameServerId,\n                moduleId,\n            });\n            takaro.log.info(`[Translator State] ${eventContext}: Active translation state CREATED as: ${isActive}`);\n        }\n    } catch (error) {\n        takaro.log.error(`[Translator State] ${eventContext}: Failed to update/create active state variable: ${error.message}`);\n    }\n}\n\nasync function main() {\n    const { gameServerId, module: mod, player } = data;\n    const eventContext = \"Player disconnected\"; // Specific context for this hook\n\n    // This hook is specifically for 'player-disconnected'\n    // The 'player' object in `data` for a disconnect event usually refers to the player who disconnected.\n    takaro.log.info(`[Translator State] ${eventContext} Hook triggered for player: ${player?.name || 'N/A (Player data might not be fully available or relevant for disconnect event context)'}.`);\n\n    // When a player disconnects, we re-evaluate the state based on who is *still* online.\n    await checkAndSetTranslatorActiveState(gameServerId, mod, eventContext);\n}\n\nawait main();\n"
        },
        {
          "name": "trasnslator active",
          "eventType": "player-connected",
          "description": "Hook for player-connected events",
          "regex": "takaro-hook-regex-placeholder",
          "function": "import { takaro, data } from '@takaro/helpers';\n\nconst LANGUAGE_VARIABLE_KEY = 'user_language';\nconst TRANSLATION_ENABLED_KEY = 'translation_enabled';\nconst TRANSLATOR_ACTIVE_STATE_KEY = 'translator_active_state'; // Stores 'true' or 'false'\n\nasync function checkAndSetTranslatorActiveState(gameServerId, mod) {\n    takaro.log.info('[Translator State] Player connected: Attempting to update active translation state...');\n\n    let rawOnlinePlayers;\n    try {\n        rawOnlinePlayers = (await takaro.gameserver.gameServerControllerGetPlayers(gameServerId)).data.data;\n    } catch (error) {\n        takaro.log.error(`[Translator State] Player connected: Error fetching online players: ${error.message}. Cannot update state.`);\n        return;\n    }\n\n    if (!rawOnlinePlayers || rawOnlinePlayers.length === 0) {\n        takaro.log.info('[Translator State] Player connected: No players reported online. Setting active translation state to false.');\n        await updateTranslatorActiveState(gameServerId, mod.moduleId, false);\n        return;\n    }\n\n    const onlinePlayerTakaroDetails = [];\n    for (const rawP of rawOnlinePlayers) {\n        const linkingId = rawP.steamId || rawP.platformId || rawP.id; // Adjust based on game server data\n        if (!linkingId) continue;\n\n        try {\n            let filter = {};\n            // Define filter based on available ID, e.g., steamId, platformId\n            if (rawP.steamId) filter = { steamId: [rawP.steamId.toString()] };\n            else if (rawP.platformId) filter = { platformId: [rawP.platformId.toString()] }; // Example\n            else if (rawP.id && typeof rawP.id === 'string' && rawP.id.length > 10) filter = { steamId: [rawP.id.toString()] }; // Fallback\n            else continue;\n\n            const playerRes = await takaro.player.playerControllerSearch({ filters: filter });\n            if (playerRes.data.data.length > 0) {\n                onlinePlayerTakaroDetails.push(playerRes.data.data[0]);\n            }\n        } catch (e) {\n            takaro.log.warn(`[Translator State] Player connected: Error searching for Takaro player with linking ID ${linkingId}: ${e.message}`);\n        }\n    }\n\n    if (onlinePlayerTakaroDetails.length < 1) {\n        takaro.log.info('[Translator State] Player connected: Fewer than 1 resolved Takaro players online. Setting active translation state to false.');\n        await updateTranslatorActiveState(gameServerId, mod.moduleId, false);\n        return;\n    }\n\n    const defaultLang = (mod.userConfig.defaultLanguage || 'en').toLowerCase();\n    const uniqueEnabledLanguages = new Set();\n    let translationPotentiallyNeeded = false;\n\n    for (const p of onlinePlayerTakaroDetails) {\n        try {\n            const enabledVar = await takaro.variable.variableControllerSearch({\n                filters: { key: [TRANSLATION_ENABLED_KEY], gameServerId, playerId: [p.id], moduleId: [mod.moduleId] }\n            });\n            let isPlayerEnabled = mod.userConfig.enabledByDefault ?? false;\n            if (enabledVar.data.data.length > 0 && enabledVar.data.data[0].value) {\n                isPlayerEnabled = enabledVar.data.data[0].value === 'true';\n            }\n\n            if (!isPlayerEnabled) {\n                continue;\n            }\n\n            const langVar = await takaro.variable.variableControllerSearch({\n                filters: { key: [LANGUAGE_VARIABLE_KEY], gameServerId, playerId: [p.id], moduleId: [mod.moduleId] }\n            });\n            let playerLang = defaultLang;\n            if (langVar.data.data.length > 0 && langVar.data.data[0].value) {\n                playerLang = langVar.data.data[0].value.toLowerCase();\n            }\n            uniqueEnabledLanguages.add(playerLang);\n\n        } catch (e) {\n            takaro.log.warn(`[Translator State] Player connected: Error fetching language/enabled status for player ${p.name} (ID ${p.id}): ${e.message}.`);\n            uniqueEnabledLanguages.add(defaultLang);\n        }\n    }\n\n    if (uniqueEnabledLanguages.size === 0 && onlinePlayerTakaroDetails.length > 0) {\n        takaro.log.info('[Translator State] Player connected: All online players have translation disabled. Setting active state to false.');\n        translationPotentiallyNeeded = false;\n    } else if (uniqueEnabledLanguages.size > 1) {\n        takaro.log.info(`[Translator State] Player connected: Multiple (${uniqueEnabledLanguages.size}) unique languages among enabled players. Setting active state to true.`);\n        translationPotentiallyNeeded = true;\n    } else if (uniqueEnabledLanguages.size === 1) {\n        const singleLang = [...uniqueEnabledLanguages][0];\n        if (singleLang !== defaultLang) {\n            takaro.log.info(`[Translator State] Player connected: Single unique language (\"${singleLang}\") among enabled players, different from default (\"${defaultLang}\"). Setting active state to true.`);\n            translationPotentiallyNeeded = true;\n        } else {\n            takaro.log.info(`[Translator State] Player connected: All enabled online players use the default language (\"${defaultLang}\"). Setting active state to false.`);\n            translationPotentiallyNeeded = false;\n        }\n    } else {\n        takaro.log.info('[Translator State] Player connected: No players with translation enabled or no unique languages identified. Setting active state to false.');\n        translationPotentiallyNeeded = false;\n    }\n\n    await updateTranslatorActiveState(gameServerId, mod.moduleId, translationPotentiallyNeeded);\n}\n\nasync function updateTranslatorActiveState(gameServerId, moduleId, isActive) {\n    try {\n        const existingStateVar = await takaro.variable.variableControllerSearch({\n            filters: { key: [TRANSLATOR_ACTIVE_STATE_KEY], gameServerId, moduleId }\n        });\n\n        if (existingStateVar.data.data.length > 0) {\n            const variableId = existingStateVar.data.data[0].id;\n            if (existingStateVar.data.data[0].value !== isActive.toString()) {\n                await takaro.variable.variableControllerUpdate(variableId, { value: isActive.toString() });\n                takaro.log.info(`[Translator State] Player connected: Active translation state UPDATED to: ${isActive}`);\n            } else {\n                takaro.log.info(`[Translator State] Player connected: Active translation state is already: ${isActive}. No update needed.`);\n            }\n        } else {\n            await takaro.variable.variableControllerCreate({\n                key: TRANSLATOR_ACTIVE_STATE_KEY,\n                value: isActive.toString(),\n                gameServerId,\n                moduleId,\n            });\n            takaro.log.info(`[Translator State] Player connected: Active translation state CREATED as: ${isActive}`);\n        }\n    } catch (error) {\n        takaro.log.error(`[Translator State] Player connected: Failed to update/create active state variable: ${error.message}`);\n    }\n}\n\nasync function main() {\n    const { gameServerId, module: mod, player } = data;\n\n    // This hook is specifically for 'player-connected'\n    takaro.log.info(`[Translator State] Player Connected Hook triggered for player: ${player?.name || 'N/A (Player data might not be fully available in this event payload for all game types)'}.`);\n\n    // The delay was removed as setTimeout is not available.\n    // If player data (like language variables) isn't immediately consistent upon connection,\n    // this hook might occasionally get a slightly outdated view.\n    // However, the state will be corrected on the next player connect/disconnect event.\n\n    await checkAndSetTranslatorActiveState(gameServerId, mod);\n}\n\nawait main();\n"
        },
        {
          "name": "auto-translate",
          "eventType": "chat-message",
          "description": "Hook for chat-message events",
          "regex": null,
          "function": "import { takaro, data, TakaroUserError } from '@takaro/helpers';\n\nconst LANGUAGE_VARIABLE_KEY = 'user_language';\nconst TRANSLATION_ENABLED_KEY = 'translation_enabled';\nconst TRANSLATOR_ACTIVE_STATE_KEY = 'translator_active_state'; // For checking global state\n\nasync function main() {\n    const { gameServerId, module: mod, eventData } = data;\n\n    // Check global active state first\n    try {\n        const activeStateVar = await takaro.variable.variableControllerSearch({\n            filters: { key: [TRANSLATOR_ACTIVE_STATE_KEY], gameServerId, moduleId: [mod.moduleId] }\n        });\n        if (activeStateVar?.data?.data?.length > 0 && activeStateVar.data.data[0].value === 'false') {\n            takaro.log.info('[Translator Auto-Translate] Globally inactive based on player language diversity. Skipping message processing.');\n            return;\n        }\n        if (!activeStateVar?.data?.data?.length) { // Variable not found\n            takaro.log.warn(`[Translator Auto-Translate] Global active state variable \"${TRANSLATOR_ACTIVE_STATE_KEY}\" not found. Proceeding, but connect/disconnect hooks may not be running or initializing state.`);\n            // To be safe, if the state variable isn't set, it might be better to assume 'false' or trigger a state check.\n            // For now, it proceeds if not found or not 'false'.\n        }\n    } catch (error) {\n        takaro.log.error(`[Translator Auto-Translate] Error checking global active translation state: ${error.message}. Proceeding with caution.`);\n    }\n\n    // Original code logic starts here\n    if (!mod.userConfig.googleApiKey) {\n        takaro.log.error('[Translator Auto-Translate] Not configured: missing Google API key.');\n        return;\n    }\n\n    const msg = eventData.msg;\n    if (typeof msg !== 'string' || !msg.trim()) {\n        takaro.log.debug('[Translator Auto-Translate] Msg is not a string or empty, skipping.');\n        return;\n    }\n\n    try {\n        const prefixSetting = await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId);\n        const prefix = prefixSetting?.data?.data?.value;\n        if (prefix && msg.startsWith(prefix)) {\n            takaro.log.debug(`[Translator Auto-Translate] Skipping command message: \"${msg}\"`);\n            return;\n        }\n    } catch (error) {\n        takaro.log.warn(`[Translator Auto-Translate] Error fetching command prefix: ${error.message}. May translate commands.`);\n    }\n\n    if (!data.player || !data.player.id) {\n        takaro.log.info('[Translator Auto-Translate] Skipping message: no player or player.id missing in event data.');\n        return;\n    }\n\n    const senderName = data.player.name;\n    const senderId = data.player.id;\n    const senderSteamId = data.player.steamId; // Assuming this is available and used for sender comparison\n    const messageToTranslate = msg;\n\n    const maxMessageLength = mod.userConfig.maxMessageLength || 500;\n    if (messageToTranslate.length > maxMessageLength) {\n        takaro.log.info(`[Translator Auto-Translate] Message from ${senderName} exceeds max length (${messageToTranslate.length}/${maxMessageLength}). Skipping.`);\n        return;\n    }\n\n    takaro.log.debug(`[Translator Auto-Translate] Processing message from ${senderName}: \"${messageToTranslate}\"`);\n\n    let onlinePlayers;\n    try {\n        onlinePlayers = (await takaro.gameserver.gameServerControllerGetPlayers(gameServerId))?.data?.data;\n        if (!onlinePlayers) {\n            takaro.log.error(\"[Translator Auto-Translate] Failed to get online players list or list is empty.\");\n            return;\n        }\n    } catch (error) {\n        takaro.log.error(`[Translator Auto-Translate] Error fetching online players list: ${error.message}`);\n        return;\n    }\n\n    takaro.log.debug(`[Translator Auto-Translate] Found ${onlinePlayers.length} online players.`);\n\n    let sourceLanguage;\n    try {\n        sourceLanguage = await detectLanguage(messageToTranslate, mod.userConfig.googleApiKey);\n        takaro.log.info(`[Translator Auto-Translate] Detected source language: \"${sourceLanguage}\" for message from ${senderName}.`);\n    } catch (error) {\n        takaro.log.error(`[Translator Auto-Translate] Language detection error: ${error.message}. Defaulting source to 'en'.`);\n        sourceLanguage = 'en';\n    }\n\n    const translationPromises = onlinePlayers.map(async (playerInfo) => {\n        // playerInfo is from gameServerControllerGetPlayers, may not be full Takaro Player DTO\n        // It typically contains platform-specific IDs like steamId, and gameId for PMs.\n        const recipientLinkingId = playerInfo.steamId || playerInfo.platformId || playerInfo.id;\n        const recipientGameIdForPM = playerInfo.gameId || playerInfo.id || playerInfo.entityId;\n\n        if (!recipientLinkingId || !recipientGameIdForPM) {\n            takaro.log.warn(`[Translator Auto-Translate] Skipping playerInfo due to missing linkingId or gameSpecificIdForPM: ${JSON.stringify(playerInfo)}`);\n            return null;\n        }\n\n        // Skip the sender\n        if (senderSteamId && recipientLinkingId === senderSteamId) { // Compare relevant IDs\n            takaro.log.debug(`[Translator Auto-Translate] Skipping translation for message sender: ${playerInfo.name || recipientLinkingId}`);\n            return null;\n        }\n\n        try {\n            let filter = {};\n            if (playerInfo.steamId) filter = { steamId: [playerInfo.steamId.toString()] };\n            else if (playerInfo.platformId) filter = { platformId: [playerInfo.platformId.toString()] };\n            else filter = { steamId: [recipientLinkingId.toString()] }; // Fallback if only generic id\n\n            const playerRes = await takaro.player.playerControllerSearch({ filters: filter });\n\n            if (!playerRes?.data?.data?.length) {\n                takaro.log.debug(`[Translator Auto-Translate] Could not find Takaro player for linking ID: ${recipientLinkingId}`);\n                return null;\n            }\n            const receiverPlayer = playerRes.data.data[0]; // This is Takaro Player DTO\n\n            const enabledRes = await takaro.variable.variableControllerSearch({\n                filters: { key: [TRANSLATION_ENABLED_KEY], gameServerId, playerId: [receiverPlayer.id], moduleId: [mod.moduleId] }\n            });\n            // Respect enabledByDefault from config if player has no specific setting\n            let isReceiverEnabled = mod.userConfig.enabledByDefault ?? false;\n            if (enabledRes?.data?.data?.length > 0 && enabledRes.data.data[0].value) {\n                isReceiverEnabled = enabledRes.data.data[0].value === 'true';\n            }\n\n            if (!isReceiverEnabled) {\n                takaro.log.debug(`[Translator Auto-Translate] Player ${receiverPlayer.name} has translations disabled, skipping.`);\n                return null;\n            }\n\n            const langVar = await takaro.variable.variableControllerSearch({\n                filters: { key: [LANGUAGE_VARIABLE_KEY], gameServerId, playerId: [receiverPlayer.id], moduleId: [mod.moduleId] }\n            });\n            // Use defaultLanguage from config if player has no specific setting\n            let targetLanguage = (mod.userConfig.defaultLanguage || 'en').toLowerCase();\n            if (langVar?.data?.data?.length > 0 && langVar.data.data[0].value) {\n                targetLanguage = langVar.data.data[0].value.toLowerCase();\n            }\n\n            takaro.log.debug(`[Translator Auto-Translate] Processing for ${receiverPlayer.name} (lang: ${targetLanguage}). Source: ${sourceLanguage}.`);\n\n            if (targetLanguage === sourceLanguage.toLowerCase()) { // Ensure case-insensitive compare\n                takaro.log.info(`[Translator Auto-Translate] Skipping translation for ${receiverPlayer.name} - target language (${targetLanguage}) is same as source.`);\n                return null;\n            }\n\n            const translation = await translateText(messageToTranslate, targetLanguage, mod.userConfig.googleApiKey);\n\n            if (!translation || translation === '[Translation error]' || translation.trim().toLowerCase() === messageToTranslate.trim().toLowerCase()) {\n                takaro.log.debug(`[Translator Auto-Translate] Translation no-op or error for ${receiverPlayer.name}. Translation: \"${translation}\"`);\n                return null;\n            }\n\n            // Send PM\n            await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n                message: `[${senderName} (${sourceLanguage})]: ${translation}`,\n                opts: { recipient: { gameId: recipientGameIdForPM.toString() } }\n            });\n            takaro.log.info(`[Translator Auto-Translate] Sent translated message to ${receiverPlayer.name} (GameID: ${recipientGameIdForPM}).`);\n            return true;\n        } catch (error) {\n            takaro.log.error(`[Translator Auto-Translate] Error processing player ${playerInfo.name || recipientLinkingId} for translation: ${error.message || error}`);\n            return null;\n        }\n    });\n\n    await Promise.allSettled(translationPromises);\n    takaro.log.info(\"[Translator Auto-Translate] Processing cycle complete.\");\n}\n\nasync function detectLanguage(text, apiKey) {\n    if (!text || !text.trim()) {\n        takaro.log.debug(\"[Translator DetectHelper] Text for language detection is empty.\");\n        throw new Error(\"Text for language detection is empty.\");\n    }\n    try {\n        const response = await takaro.axios.post(\n            `https://translation.googleapis.com/language/translate/v2/detect?key=${apiKey}`,\n            { q: text },\n            { headers: { 'Content-Type': 'application/json' } }\n        );\n        if (response?.data?.error) {\n            takaro.log.warn(`[Translator DetectHelper] Detection API error: ${response.data.error.message}`);\n            throw new Error(response.data.error.message);\n        }\n        if (!response?.data?.data?.detections?.[0]?.[0]?.language) {\n            takaro.log.warn(\"[Translator DetectHelper] Invalid response format from language detection API.\");\n            throw new Error(\"Invalid response format from language detection API\");\n        }\n        return response.data.data.detections[0][0].language;\n    } catch (error) {\n        takaro.log.error(`[Translator DetectHelper] Language detection API call failed: ${error.message || error}`);\n        throw error;\n    }\n}\n\nasync function translateText(text, targetLanguage, apiKey) {\n    if (!text || !text.trim()) {\n        takaro.log.debug(`[Translator TranslateHelper] Text for translation to ${targetLanguage} is empty.`);\n        return \"\";\n    }\n    try {\n        const response = await takaro.axios.post(\n            `https://translation.googleapis.com/language/translate/v2?key=${apiKey}`,\n            { q: text, target: targetLanguage, format: \"text\" },\n            { headers: { 'Content-Type': 'application/json' } }\n        );\n        const result = response?.data;\n        if (result?.error) {\n            takaro.log.warn(`[Translator TranslateHelper] Translation API error (target: ${targetLanguage}): ${result.error.message}`);\n            throw new Error(result.error.message);\n        }\n        // Allow empty string as a valid translation result\n        if (!result?.data?.translations?.[0] || typeof result.data.translations[0].translatedText === 'undefined') {\n            takaro.log.warn(`[Translator TranslateHelper] Invalid response format or translatedText missing (target: ${targetLanguage}).`);\n            throw new Error(\"Invalid response format from translation API\");\n        }\n        return result.data.translations[0].translatedText;\n    } catch (error) {\n        takaro.log.error(`[Translator TranslateHelper] Translation API call failed (target: ${targetLanguage}): ${error.message || error}`);\n        return '[Translation error]';\n    }\n}\n\nawait main();\n"
        }
      ],
      "cronJobs": [],
      "functions": [],
      "permissions": [
        {
          "permission": "TRANSLATE_PERMISSION",
          "friendlyName": "TRANSLATE_PERMISSION",
          "description": "Give players Translate permission",
          "canHaveCount": false
        },
        {
          "permission": "TRANSLATE_ADMIN",
          "friendlyName": "Admin Set Language",
          "description": "Admins can set language",
          "canHaveCount": false
        }
      ]
    }
  ]
}