Skip to content

Extension: pack

Arcensoth edited this page Aug 30, 2021 · 5 revisions

pack

Compile messages into data packs and resource packs.

Options

  • timeout: The maximum number of seconds that builds are allowed to run for. Defaults to 5 seconds.
  • stacktraces: A boolean indicating if the bot should reply with the full exception tracebacks. Defaults to false.
  • <beet config option>: Edit the beet configuration used by the bot.

Commands

The extension provides a single pack command. The command takes an optional name argument that's used for changing the name of the output data pack or resource pack. The command is available to all users.

Examples usage:

.pack
`@function demo:foo`
```
say hello
```

By default, the zipfile is named after the username of the message author. You can change it by providing a name explicitly:

.pack greeting
`@function demo:foo`
```
say hello
```

The message is interpreted by lectern to extract data pack and resource pack fragments. Note that the extension doesn't run the build on the main thread so the bot should remain responsive even if someone is generating huge data packs or resource packs.

Beet plugins

You can use beet plugins to customize the build process. For example, if you want to use the syntactic extensions provided by the beet.contrib.hangman plugin you can configure the bot like this:

{
  "command_prefix": ".",
  "extensions": [
    {
      "name": "commanderbot.ext.pack",
      "options": {
        "pipeline": ["beet.contrib.hangman"],
        "meta": {
          "hangman": {
            "match": ["*"]
          }
        }
      }
    }
  ]
}

This allows the bot to understand messages like these:

.pack
`@function demo:foo`
```
execute as @a run commands
    say hello
    say world
```

Note that you can use the extend option to separate the beet config into its own file. This also allows the bot to automatically reload the beet config if you change it.

// bot.json
{
  "command_prefix": ".",
  "extensions": [
    {
      "name": "commanderbot.ext.pack",
      "options": {
        "extend": ["beet.json"]
      }
    }
  ]
}
// beet.json
{
  "pipeline": ["beet.contrib.hangman"],
  "meta": {
    "hangman": {
      "match": ["*"]
    }
  }
}

Plugin whitelist

To turn messages into data packs and resource packs, the command feeds untrusted input into the beet pipeline. This is usually fine, however lectern has a @require directive that can include extra plugins dynamically. If you decide to enable it, you can restrict the plugins available by specifying the whitelist option:

{
  "command_prefix": ".",
  "extensions": [
    {
      "name": "commanderbot.ext.pack",
      "options": {
        "whitelist": [
          "lectern.contrib.require",
          "commanderbot.ext.pack.pack_generate"
        ],
        "require": ["lectern.contrib.require"]
      }
    }
  ]
}

Note that you'll always need to whitelist commanderbot.ext.pack.pack_generate as it's required by the bot. Of course if you add other plugins to the pipeline you need to add them to the whitelist as well.

{
  "command_prefix": ".",
  "extensions": [
    {
      "name": "commanderbot.ext.pack",
      "options": {
        "whitelist": [
          "lectern.contrib.require",
          "beet.contrib.hangman",
          "commanderbot.ext.pack.pack_generate"
        ],
        "require": ["lectern.contrib.require"],
        "pipeline": ["beet.contrib.hangman"],
        "meta": {
          "hangman": {
            "match": ["*"]
          }
        }
      }
    }
  ]
}

Enabling templates

The beet.contrib.render plugin lets you process messages with Jinja. This can be really useful but really unsafe too. By default there's no mitigation that prevents Jinja from running malicious input. Therefore, you should only activate templates with the beet.contrib.template_sandbox plugin to enable the Jinja Sandbox.

{
  "command_prefix": ".",
  "extensions": [
    {
      "name": "commanderbot.ext.pack",
      "options": {
        "require": ["beet.contrib.template_sandbox"],
        "pipeline": ["beet.contrib.render"],
        "meta": {
          "render": {
            "data_pack": {
              "functions": ["*"]
            }
          }
        }
      }
    }
  ]
}

The beet.contrib.template_sandbox plugin resets the template environment so it must be executed before the beet.contrib.render plugin by using the require option.

With the sandbox in place, everything should now be fine. The only thing to watch out for is that people can still try to slow the host machine down by generating huge data packs or resource packs:

.pack
`@function demo:foo`
```
#!for i in range(100000)
#!for j in range(100000)
say {{ i + j }}
#!endfor
#!endfor
```

You can adjust the timeout option to stop processing abusive messages:

{
  "command_prefix": ".",
  "extensions": [
    {
      "name": "commanderbot.ext.pack",
      "options": {
        "timeout": 3
      }
    }
  ]
}

By default the build stops if a message takes more than 5 seconds to process. When adjusting the timeout keep in mind that lectern supports including files from urls so if you care about being able to use this feature you should use a timeout that gives lectern enough time to download remote files.

It's worth mentioning that everything here also applies if you're using lectern.contrib.define or lectern.contrib.script. These two plugins add directives that use Jinja so you should enable the sandbox if you use them with a public-facing bot.

Example setup

The following example shows how to enable templating alongside other features that might be interesting for processing messages. It uses a custom timeout and a separate beet.json for configuring the pipeline.

// bot.json
{
  "command_prefix": ".",
  "extensions": [
    {
      "name": "commanderbot.ext.pack",
      "options": {
        "timeout": 3,
        "extend": ["beet.json"]
      }
    }
  ]
}
// beet.json
{
  "require": [
    "beet.contrib.template_sandbox",
    "beet.contrib.inline_function",
    "beet.contrib.inline_function_tag",
    "beet.contrib.dundervar",
    "lectern.contrib.relative_location",
    "lectern.contrib.define",
    "lectern.contrib.script"
  ],
  "pipeline": [
    "beet.contrib.render",
    "beet.contrib.hangman",
    "beet.contrib.relative_function_path",
    "beet.contrib.scoreboard"
  ],
  "meta": {
    "render": {
      "data_pack": {
        "functions": ["*"]
      }
    },
    "hangman": {
      "match": ["*"]
    }
  }
}

First, in the require option we're using the template sandbox to make it safe to enable templating, as well as plugins that provide #!function and #!tag in templates, __dunder__ interpolation, resource locations without namespaces @function foo, and the @define and @script directives. In the pipeline, we start by rendering templates, then we fold hanging indents in function files with the hangman plugin, we resolve relative function paths, and finally the scoreboard plugin takes care of adding objectives generated with generate_objective() to the data pack.

If you want to enable the @require directive you should configure the whitelist. The whitelist needs to include all the plugins specified in the config file, plus the one used by the bot.

// beet.json
{
  "whitelist": [
    "beet.contrib.template_sandbox",
    "beet.contrib.inline_function",
    "beet.contrib.inline_function_tag",
    "beet.contrib.dundervar",
    "lectern.contrib.relative_location",
    "lectern.contrib.require",
    "lectern.contrib.define",
    "lectern.contrib.script",
    "beet.contrib.render",
    "beet.contrib.hangman",
    "beet.contrib.scoreboard",
    "beet.contrib.relative_function_path",
    "commanderbot.ext.pack.pack_generate",
    "beet.contrib.yellow_shulker_box"
  ],
  "require": [
    "beet.contrib.template_sandbox",
    "beet.contrib.inline_function",
    "beet.contrib.inline_function_tag",
    "beet.contrib.dundervar",
    "lectern.contrib.relative_location",
    "lectern.contrib.require",
    "lectern.contrib.define",
    "lectern.contrib.script"
  ],
  "pipeline": [
    "beet.contrib.render",
    "beet.contrib.hangman",
    "beet.contrib.relative_function_path",
    "beet.contrib.scoreboard"
  ],
  "meta": {
    "render": {
      "data_pack": {
        "functions": ["*"]
      }
    },
    "hangman": {
      "match": ["*"]
    }
  }
}
Clone this wiki locally