Skip to content

Commit

Permalink
initial support for lua, WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
terminaldweller committed May 31, 2024
1 parent 215db0c commit 0e1a3e5
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 13 deletions.
3 changes: 2 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ run:
modules-download-mode: readonly
allow-parallel-runners: true
allow-serial-runners: true
go: '1.21'
go: '1.22.3'
linters-settings:
depguard:
rules:
Expand All @@ -20,3 +20,4 @@ linters-settings:
- github.com/jackc/pgx/v5
- github.com/jackc/pgx/v5/pgxpool
- github.com/jackc/pgx/v5/pgtype
- github.com/yuin/gopher-lua
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ The formatter to use. This tells chroma how to generate the color in the output.
- `terminal8` for 8-color terminals
- `terminal16` for 16-color terminals
- `terminal256` for 256-color terminals
- `terminal16m` for treucolor terminals
- `terminal16m` for truecolor terminals
- `html` for HTML output

**_NOTE_**: please note that the terminal formatters will increase the size of the IRC event. Depending on the IRC server, this may or may not be a problem.
Expand Down Expand Up @@ -528,7 +528,12 @@ go build
## FAQ

- I end up with color escape sequences getting printed at the end of a line/begging of the next line. What gives?
This is happening because you have reached the message limit on irc which 512 for the event. This practically leaves around 390-400 character left for the message itself. Certain ircds allow for bigger sizes and certain clients might do. But most ircds dont send `linelen` to the clients. In a closed-loop situation where you control everything, as in, the ircd and all the clients(i.e. A private irc network), you can try to increase the `linelen` for the ircd and the client. Please note that the client in this case is girc. You irc client can have its own set of limits too. The 512 limit is hardcoded in girc. You can vendor the build or use the vendored dockerfile, change the hard limit and run milla with an increased limit. Needless to say, you can try to use a chromaFormatter that produces less characters which is basically not using truecolor or `terminal16m`.
This is happening because you have reached the message limit on irc which 512 for the event. This practically leaves around 390-400 character left for the message itself. Certain ircds allow for bigger sizes and certain clients might do. But most ircds don't send `linelen` to the clients. In a closed-loop situation where you control everything, as in, the ircd and all the clients(i.e. A private irc network), you can try to increase the `linelen` for the ircd and the client. Please note that the client in this case is girc. You irc client can have its own set of limits too. The 512 limit is hardcoded in girc. You can vendor the build or use the vendored dockerfile, change the hard limit and run milla with an increased limit. Needless to say, you can try to use a `chromaFormatter` that produces less characters which is basically not using truecolor or `terminal16m`.

## Resources

- [OpenRSS](https://openrss.org/)
- [Google Alerts](https://www.google.com/alerts)

## Thanks

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/jackc/pgx/v5 v5.5.5
github.com/lrstanley/girc v0.0.0-20240125042120-9add3166e52e
github.com/sashabaranov/go-openai v1.19.3
github.com/yuin/gopher-lua v1.1.1
golang.org/x/net v0.24.0
google.golang.org/api v0.176.1
)
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
Expand Down
22 changes: 12 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ type TomlConfig struct {
WebIRCGateway string `toml:"webIRCGateway"`
WebIRCHostname string `toml:"webIRCHostname"`
WebIRCAddress string `toml:"webIRCAddress"`
PluginPath string `toml:"pluginPath"`
CustomCommands map[string]CustomCommand `toml:"customCommands"`
Temp float64 `toml:"temp"`
RequestTimeout int `toml:"requestTimeout"`
Expand Down Expand Up @@ -353,7 +354,7 @@ func setFieldByName(v reflect.Value, field string, value string) error {

func byteToMByte(bytes uint64,
) uint64 {
return bytes / 1024 / 1024
return bytes / 1024 / 1024 //nolint: mnd,gomnd
}

func handleCustomCommand(
Expand All @@ -363,7 +364,8 @@ func handleCustomCommand(
appConfig *TomlConfig,
) {
log.Println(args)
if len(args) < 2 {

if len(args) < 2 { //nolint: mnd,gomnd
client.Cmd.Reply(event, errNotEnoughArgs.Error())

return
Expand Down Expand Up @@ -484,7 +486,7 @@ func runCommand(
case "help":
sendToIRC(client, event, getHelpString(), "noop")
case "set":
if len(args) < 3 {
if len(args) < 3 { //nolint: mnd,gomnd
client.Cmd.Reply(event, errNotEnoughArgs.Error())

break
Expand All @@ -495,7 +497,7 @@ func runCommand(
client.Cmd.Reply(event, err.Error())
}
case "get":
if len(args) < 2 {
if len(args) < 2 { //nolint: mnd,gomnd
client.Cmd.Reply(event, errNotEnoughArgs.Error())

break
Expand All @@ -514,12 +516,12 @@ func runCommand(

client.Cmd.Reply(event, fmt.Sprintf("%v", field.Interface()))
case "getall":
v := reflect.ValueOf(*appConfig)
t := v.Type()
value := reflect.ValueOf(*appConfig)
t := value.Type()

for i := 0; i < v.NumField(); i++ {
for i := range value.NumField() {
field := t.Field(i)
fieldValue := v.Field(i).Interface()
fieldValue := value.Field(i).Interface()
client.Cmd.Reply(event, fmt.Sprintf("%s: %v", field.Name, fieldValue))
}
case "memstats":
Expand All @@ -535,7 +537,7 @@ func runCommand(
break
}

if len(args) < 2 {
if len(args) < 2 { //nolint: mnd,gomnd
client.Cmd.Reply(event, errNotEnoughArgs.Error())

break
Expand All @@ -547,7 +549,7 @@ func runCommand(
break
}

if len(args) < 2 {
if len(args) < 2 { //nolint: mnd,gomnd
client.Cmd.Reply(event, errNotEnoughArgs.Error())

break
Expand Down
179 changes: 179 additions & 0 deletions plugins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package main

import (
"fmt"
"log"
"os"
"reflect"

lua "github.com/yuin/gopher-lua"
)

func registerStrucAsLuaMetaTable[T any](
luaState *lua.LState,
checkStruct func(luaState *lua.LState) *T,
structType T,
metaTableName string,
) {
metaTable := luaState.NewTypeMetatable(metaTableName)

luaState.SetGlobal(metaTableName, metaTable)

luaState.SetField(
metaTable,
"new",
luaState.NewFunction(
newStructFunctionFactory(structType, metaTableName),
),
)

var zero [0]T

luaState.SetField(
metaTable,
"__index",
luaState.SetFuncs(
luaState.NewTable(),
luaTableGenFactory(reflect.TypeOf(zero),
checkStruct,
),
),
)
}

func newStructFunctionFactory[T any](structType T, metaTableName string) func(*lua.LState) int {
return func(luaState *lua.LState) int {
structInstance := &structType
ud := luaState.NewUserData()
ud.Value = structInstance
luaState.SetMetatable(ud, luaState.GetTypeMetatable(metaTableName))
luaState.Push(ud)

return 1
}
}

func checkStruct[T any](luaState *lua.LState) *T {
userData := luaState.CheckUserData(1)
if v, ok := userData.Value.(*T); ok {
return v
}

luaState.ArgError(1, "got wrong struct")

return nil
}

func getterSetterFactory[T any](
fieldName string,
fieldType reflect.Type,
checkStruct func(luaState *lua.LState) *T,
) func(*lua.LState) int {
return func(luaState *lua.LState) int {
genericStruct := checkStruct(luaState)

structValue := reflect.ValueOf(genericStruct).Elem()

fieldValue := structValue.FieldByName(fieldName)

if luaState.GetTop() == 2 { //nolint: mnd,gomnd
switch fieldType.Kind() {
case reflect.String:
fieldValue.SetString(luaState.CheckString(2)) //nolint: mnd,gomnd
case reflect.Float64:
fieldValue.SetFloat(float64(luaState.CheckNumber(2))) //nolint: mnd,gomnd
case reflect.Float32:
fieldValue.SetFloat(float64(luaState.CheckNumber(2))) //nolint: mnd,gomnd
case reflect.Int8:
fieldValue.SetInt(int64(luaState.CheckInt(2))) //nolint: mnd,gomnd
case reflect.Int16:
fieldValue.SetInt(int64(luaState.CheckInt(2))) //nolint: mnd,gomnd
case reflect.Int:
fieldValue.SetInt(int64(luaState.CheckInt(2))) //nolint: mnd,gomnd
case reflect.Int32:
fieldValue.SetInt(int64(luaState.CheckInt(2))) //nolint: mnd,gomnd
case reflect.Int64:
fieldValue.SetInt(int64(luaState.CheckInt(2))) //nolint: mnd,gomnd
case reflect.Bool:
fieldValue.SetBool(luaState.CheckBool(2)) //nolint: mnd,gomnd
default:
log.Print("unsupported type")
}

return 0
}

switch fieldType.Kind() {
case reflect.String:
luaState.Push(lua.LString(fieldValue.Interface().(string)))
case reflect.Float64:
luaState.Push(lua.LNumber(fieldValue.Interface().(float64)))
case reflect.Float32:
luaState.Push(lua.LNumber(fieldValue.Float()))
case reflect.Int8:
luaState.Push(lua.LNumber(fieldValue.Int()))
case reflect.Int16:
luaState.Push(lua.LNumber(fieldValue.Int()))
case reflect.Int:
luaState.Push(lua.LNumber(fieldValue.Int()))
case reflect.Int32:
luaState.Push(lua.LNumber(fieldValue.Int()))
case reflect.Int64:
luaState.Push(lua.LNumber(fieldValue.Int()))
case reflect.Bool:
luaState.Push(lua.LBool(fieldValue.Bool()))
default:
log.Print("unsupported type")
}

return 1
}
}

func luaTableGenFactory[T any](
structType reflect.Type,
checkStructType func(luaState *lua.LState) *T) map[string]lua.LGFunction {
tableMethods := make(map[string]lua.LGFunction)

for _, field := range reflect.VisibleFields(structType) {
tableMethods[field.Name] = getterSetterFactory(field.Name, field.Type, checkStructType)
}

return tableMethods
}

func RegisterCustomLuaTypes(luaState *lua.LState) {
registerStrucAsLuaMetaTable(luaState, checkStruct, TomlConfig{}, "toml_config")
registerStrucAsLuaMetaTable(luaState, checkStruct, CustomCommand{}, "custom_command")
}

func returnAllPlugins(pluginPath string) ([]string, error) {
pluginList := make([]string, 0)

files, err := os.ReadDir(pluginPath)
if err != nil {
return pluginList, fmt.Errorf("Error reading plugins directory: %v", err)
}

for _, file := range files {
pluginList = append(pluginList, file.Name())
}

return pluginList, nil
}

func LoadAllPlugins(appConfig *TomlConfig) {
luaState := lua.NewState()
defer luaState.Close()

RegisterCustomLuaTypes(luaState)

allPlugins, err := returnAllPlugins(appConfig.PluginPath)
if err != nil {
luaState.Close()

log.Fatal(err) //nolint: gocritic
}

log.Println(allPlugins)
}
23 changes: 23 additions & 0 deletions plugins_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"testing"

lua "github.com/yuin/gopher-lua"
)

func MetaTableTest(t *testing.T) {
luaState := lua.NewState()
defer luaState.Close()

RegisterCustomLuaTypes(luaState)

if err := luaState.DoString(`
config = toml_config.new()
print(config:IrcServer())
config:TrcServer("irc.freenode.net")
print(config:IrcServer())
`); err != nil {
t.Fatal(err)
}
}

0 comments on commit 0e1a3e5

Please sign in to comment.