aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.golangci.yml3
-rw-r--r--README.md9
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--main.go22
-rw-r--r--plugins.go179
-rw-r--r--plugins_test.go23
7 files changed, 226 insertions, 13 deletions
diff --git a/.golangci.yml b/.golangci.yml
index b72d9d8..0b58fca 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -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:
@@ -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
diff --git a/README.md b/README.md
index 520f8c2..ee46796 100644
--- a/README.md
+++ b/README.md
@@ -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.
@@ -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
diff --git a/go.mod b/go.mod
index 65be8d4..49e70f7 100644
--- a/go.mod
+++ b/go.mod
@@ -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
)
diff --git a/go.sum b/go.sum
index 1222b11..7641a04 100644
--- a/go.sum
+++ b/go.sum
@@ -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=
diff --git a/main.go b/main.go
index 6d08e63..b4ebe04 100644
--- a/main.go
+++ b/main.go
@@ -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"`
@@ -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(
@@ -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
@@ -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
@@ -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
@@ -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":
@@ -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
@@ -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
diff --git a/plugins.go b/plugins.go
new file mode 100644
index 0000000..d64f65b
--- /dev/null
+++ b/plugins.go
@@ -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)
+}
diff --git a/plugins_test.go b/plugins_test.go
new file mode 100644
index 0000000..c6cd7ef
--- /dev/null
+++ b/plugins_test.go
@@ -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)
+ }
+}