-
-
Notifications
You must be signed in to change notification settings - Fork 450
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add basic lua scripting capabilities (#4341)
The scripting capabilities is locked behind a cmake flag, and is not enabled by default. Co-authored-by: nerix <[email protected]> Co-authored-by: pajlada <[email protected]>
- Loading branch information
1 parent
5836073
commit 5ba8098
Showing
37 changed files
with
2,087 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/** @noSelfInFile */ | ||
|
||
declare module c2 { | ||
enum LogLevel { | ||
Debug, | ||
Info, | ||
Warning, | ||
Critical, | ||
} | ||
class CommandContext { | ||
words: String[]; | ||
channel_name: String; | ||
} | ||
|
||
function log(level: LogLevel, ...data: any[]): void; | ||
function register_command( | ||
name: String, | ||
handler: (ctx: CommandContext) => void | ||
): boolean; | ||
function send_msg(channel: String, text: String): boolean; | ||
function system_msg(channel: String, text: String): boolean; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
{ | ||
"$schema": "http://json-schema.org/schema", | ||
"$id": "https://raw.githubusercontent.com/Chatterino/chatterino2/master/docs/plugin-info.schema.json", | ||
"title": "Plugin info", | ||
"description": "Describes a Chatterino2 plugin (draft)", | ||
"type": "object", | ||
"additionalProperties": false, | ||
"properties": { | ||
"name": { | ||
"type": "string", | ||
"description": "Plugin name shown to the user." | ||
}, | ||
"description": { | ||
"type": "string", | ||
"description": "Plugin description shown to the user." | ||
}, | ||
"authors": { | ||
"type": "array", | ||
"description": "An array of authors of this Plugin.", | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"homepage": { | ||
"type": "string", | ||
"description": "Optional URL to your Plugin's homepage. This could be your GitHub repo for example." | ||
}, | ||
"tags": { | ||
"description": "Something that could in the future be used to find your plugin.", | ||
"type": "array", | ||
"items": { | ||
"type": "string", | ||
"examples": ["moderation", "utility", "commands"] | ||
}, | ||
"uniqueItems": true | ||
}, | ||
"version": { | ||
"type": "string", | ||
"description": "Semver version string, for more info see https://semver.org.", | ||
"examples": ["0.0.1", "1.0.0-rc.1"] | ||
}, | ||
"license": { | ||
"type": "string", | ||
"description": "A small description of your license.", | ||
"examples": ["MIT", "GPL-2.0-or-later"] | ||
} | ||
}, | ||
"required": ["name", "description", "authors", "version", "license"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
# Plugins | ||
|
||
If Chatterino is compiled with the `CHATTERINO_PLUGINS` CMake option, it can | ||
load and execute Lua files. Note that while there are attempts at making this | ||
decently safe, we cannot guarantee safety. | ||
|
||
## Plugin structure | ||
|
||
Chatterino searches for plugins in the `Plugins` directory in the app data, right next to `Settings` and `Logs`. | ||
|
||
Each plugin should have its own directory. | ||
|
||
``` | ||
Chatterino Plugins dir/ | ||
└── plugin_name/ | ||
├── init.lua | ||
└── info.json | ||
``` | ||
|
||
`init.lua` will be the file loaded when the plugin is enabled. You may load other files using [`import` global function](#importfilename=). | ||
|
||
`info.json` contains metadata about the plugin, like its name, description, | ||
authors, homepage link, tags, version, license name. The version field **must** | ||
be [semver 2.0](https://semver.org/) compliant. The general idea of `info.json` | ||
will not change however the exact contents probably will, for example with | ||
permission system ideas. | ||
Example file: | ||
|
||
```json | ||
{ | ||
"$schema": "https://raw.githubusercontent.com/Chatterino/chatterino2/master/docs/plugin-info.schema.json", | ||
"name": "Test plugin", | ||
"description": "This plugin is for testing stuff.", | ||
"authors": "Mm2PL", | ||
"homepage": "https://github.com/Chatterino/Chatterino2", | ||
"tags": ["test"], | ||
"version": "0.0.0", | ||
"license": "MIT" | ||
} | ||
``` | ||
|
||
An example plugin is available at [https://github.com/Mm2PL/Chatterino-test-plugin](https://github.com/Mm2PL/Chatterino-test-plugin) | ||
|
||
## Plugins with Typescript | ||
|
||
If you prefer, you may use [TypescriptToLua](https://typescripttolua.github.io) | ||
to typecheck your plugins. There is a `chatterino.d.ts` file describing the API | ||
in this directory. However this has several drawbacks like harder debugging at | ||
runtime. | ||
|
||
## API | ||
|
||
The following parts of the Lua standard library are loaded: | ||
|
||
- `_G` (most globals) | ||
- `table` | ||
- `string` | ||
- `math` | ||
- `utf8` | ||
|
||
The official manual for them is available [here](https://www.lua.org/manual/5.4/manual.html#6). | ||
|
||
### Chatterino API | ||
|
||
All Chatterino functions are exposed in a global table called `c2`. The following members are available: | ||
|
||
#### `log(level, args...)` | ||
|
||
Writes a message to the Chatterino log. The `level` argument should be a | ||
`LogLevel` member. All `args` should be convertible to a string with | ||
`tostring()`. | ||
|
||
Example: | ||
|
||
```lua | ||
c2.log(c2.LogLevel.Warning, "Hello, this should show up in the Chatterino log by default") | ||
|
||
c2.log(c2.LogLevel.Debug, "Hello world") | ||
-- Equivalent to doing qCDebug(chatterinoLua) << "[pluginDirectory:Plugin Name]" << "Hello, world"; from C++ | ||
``` | ||
|
||
#### `LogLevel` enum | ||
|
||
This table describes log levels available to Lua Plugins. The values behind the names may change, do not count on them. It has the following keys: | ||
|
||
- `Debug` | ||
- `Info` | ||
- `Warning` | ||
- `Critical` | ||
|
||
#### `register_command(name, handler)` | ||
|
||
Registers a new command called `name` which when executed will call `handler`. | ||
Returns `true` if everything went ok, `false` if there already exists another | ||
command with this name. | ||
|
||
Example: | ||
|
||
```lua | ||
function cmdWords(ctx) | ||
-- ctx contains: | ||
-- words - table of words supplied to the command including the trigger | ||
-- channelName - name of the channel the command is being run in | ||
c2.system_msg(ctx.channelName, "Words are: " .. table.concat(ctx.words, " ")) | ||
end | ||
|
||
c2.register_command("/words", cmdWords) | ||
``` | ||
|
||
Limitations/known issues: | ||
|
||
- Commands registered in functions, not in the global scope might not show up in the settings UI, | ||
rebuilding the window content caused by reloading another plugin will solve this. | ||
- Spaces in command names aren't handled very well (https://github.com/Chatterino/chatterino2/issues/1517). | ||
|
||
#### `send_msg(channel, text)` | ||
|
||
Sends a message to `channel` with the specified text. Also executes commands. | ||
|
||
Example: | ||
|
||
```lua | ||
function cmdShout(ctx) | ||
table.remove(ctx.words, 1) | ||
local output = table.concat(ctx.words, " ") | ||
c2.send_msg(ctx.channelName, string.upper(output)) | ||
end | ||
c2.register_command("/shout", cmdShout) | ||
``` | ||
|
||
Limitations/Known issues: | ||
|
||
- It is possible to trigger your own Lua command with this causing a potentially infinite loop. | ||
|
||
#### `system_msg(channel, text)` | ||
|
||
Creates a system message and adds it to the twitch channel specified by | ||
`channel`. Returns `true` if everything went ok, `false` otherwise. It will | ||
throw an error if the number of arguments received doesn't match what it | ||
expects. | ||
|
||
Example: | ||
|
||
```lua | ||
local ok = c2.system_msg("pajlada", "test") | ||
if (not ok) | ||
-- channel not found | ||
end | ||
``` | ||
|
||
### Changed globals | ||
|
||
#### `load(chunk [, chunkname [, mode [, env]]])` | ||
|
||
This function is only available if Chatterino is compiled in debug mode. It is meant for debugging with little exception. | ||
This function behaves really similarity to Lua's `load`, however it does not allow for bytecode to be executed. | ||
It achieves this by forcing all inputs to be encoded with `UTF-8`. | ||
|
||
See [official documentation](https://www.lua.org/manual/5.4/manual.html#pdf-load) | ||
|
||
#### `import(filename)` | ||
|
||
This function mimics Lua's `dofile` however relative paths are relative to your plugin's directory. | ||
You are restricted to loading files in your plugin's directory. You cannot load files with bytecode inside. | ||
|
||
Example: | ||
|
||
```lua | ||
import("stuff.lua") -- executes Plugins/name/stuff.lua | ||
import("./stuff.lua") -- executes Plugins/name/stuff.lua | ||
import("../stuff.lua") -- tries to load Plugins/stuff.lua and errors | ||
import("luac.out") -- tried to load Plugins/name/luac.out and errors because it contains non-utf8 data | ||
``` | ||
|
||
#### `print(Args...)` | ||
|
||
The `print` global function is equivalent to calling `c2.log(c2.LogLevel.Debug, Args...)` |
Oops, something went wrong.