diff options
Diffstat (limited to 'dynamicid')
| -rw-r--r-- | dynamicid/encoding.go | 115 | ||||
| -rw-r--r-- | dynamicid/handling.go | 74 |
2 files changed, 189 insertions, 0 deletions
diff --git a/dynamicid/encoding.go b/dynamicid/encoding.go new file mode 100644 index 0000000..23d00db --- /dev/null +++ b/dynamicid/encoding.go @@ -0,0 +1,115 @@ +package dynamicid + +import ( + "encoding/csv" + "fmt" + "reflect" + "strconv" + "strings" +) + +var ( + stringReflectType = reflect.TypeOf(string("")) + intReflectType = reflect.TypeOf(int(0)) + uintReflectType = reflect.TypeOf(uint(0)) + boolReflectType = reflect.TypeOf(bool(false)) +) + +// UnmarshallCSV record into a struct in-place +func UnmarshallCSV(data string, v any) error { + r := csv.NewReader(strings.NewReader(data)) + record, err := r.Read() + if err != nil { + return err + } + s := reflect.ValueOf(v).Elem() + t := s.Type() + if s.NumField() != len(record) { + return &ErrFieldMismatch{s.NumField(), len(record)} + } + for i := 0; i < s.NumField(); i++ { + f := s.Field(i) + if t.Field(i).Tag.Get("cid") != "-" { + switch f.Type() { + case stringReflectType: + f.SetString(record[i]) + case intReflectType: + v, err := strconv.ParseInt(record[i], 10, 0) + if err != nil { + return err + } + f.SetInt(v) + case uintReflectType: + v, err := strconv.ParseUint(record[i], 10, 0) + if err != nil { + return err + } + f.SetUint(v) + case boolReflectType: + switch record[i] { + case "0": + f.SetBool(false) + case "1": + f.SetBool(true) + default: + return &ErrUnreadable{"boolean", record[i]} + } + default: + return &ErrUnsupportedType{Type: f.Type().String()} + } + } + } + return nil +} + +// MarshallCSV from a struct +func MarshallCSV(v any) string { + s := reflect.ValueOf(v) + r := make([]string, 0) + for i := 0; i < s.NumField(); i++ { + f := s.Field(i) + switch f.Type() { + case stringReflectType: + r = append(r, f.String()) + case intReflectType: + r = append(r, strconv.FormatInt(f.Int(), 10)) + case uintReflectType: + r = append(r, strconv.FormatUint(f.Uint(), 10)) + case boolReflectType: + if f.Bool() { + r = append(r, "1") + } else { + r = append(r, "0") + } + } + } + b := new(strings.Builder) + w := csv.NewWriter(b) + w.Write(r) + w.Flush() + return b.String() +} + +type ErrFieldMismatch struct { + Expected, Found int +} + +func (e *ErrFieldMismatch) Error() string { + return fmt.Sprintf("CSV line fields mismatch. Expected %d found %d", e.Expected, e.Found) +} + +type ErrUnreadable struct { + Format, Found string +} + +func (e *ErrUnreadable) Error() string { + return fmt.Sprintf("Unreadable value as %s. Found %s", e.Format, e.Found) +} + +type ErrUnsupportedType struct { + Type string +} + +func (e *ErrUnsupportedType) Error() string { + return "Unsupported type: " + e.Type +} diff --git a/dynamicid/handling.go b/dynamicid/handling.go new file mode 100644 index 0000000..8369e27 --- /dev/null +++ b/dynamicid/handling.go @@ -0,0 +1,74 @@ +package dynamicid + +import ( + "strings" + + "github.com/anhgelus/gokord" + "github.com/anhgelus/gokord/cmd" + "github.com/nyttikord/gokord/bot" + "github.com/nyttikord/gokord/discord/types" + "github.com/nyttikord/gokord/event" + "github.com/nyttikord/gokord/interaction" +) + +func HandleDynamicMessageComponent[DynamicData any]( + b *gokord.Bot, + handler func( + bot.Session, + *event.InteractionCreate, + *interaction.MessageComponentData, + *DynamicData, *cmd.ResponseBuilder, + ), + base string, +) { + b.AddHandler(func(s bot.Session, i *event.InteractionCreate) { + if i.Type != types.InteractionMessageComponent { + return + } + data := i.MessageComponentData() + if !strings.HasPrefix(data.CustomID, base+";") { + return + } + dynamicID := data.CustomID[len(base)+1:] + dynamicData := new(DynamicData) + err := UnmarshallCSV(dynamicID, dynamicData) + if err != nil { + s.Logger().Error("Unable to parse CustomID", "error", err, "CustomID", data.CustomID, "base", base) + return + } + handler(s, i, data, dynamicData, cmd.NewResponseBuilder(s, i)) + }) +} + +func HandleDynamicModalComponent[DynamicData any]( + b *gokord.Bot, + handler func( + bot.Session, + *event.InteractionCreate, + *interaction.ModalSubmitData, + *DynamicData, + *cmd.ResponseBuilder, + ), + base string, +) { + b.AddHandler(func(s bot.Session, i *event.InteractionCreate) { + if i.Type != types.InteractionModalSubmit { + return + } + data := i.ModalSubmitData() + if strings.HasPrefix(data.CustomID, base+";") { + dynamicID := data.CustomID[len(base)+1:] + dynamicData := new(DynamicData) + err := UnmarshallCSV(dynamicID, dynamicData) + if err != nil { + s.Logger().Error("Unable to parse CustomID", "error", err, "CustomID", data.CustomID, "base", base) + return + } + handler(s, i, data, dynamicData, cmd.NewResponseBuilder(s, i)) + } + }) +} + +func FormatCustomID(base string, dynamicData any) string { + return base + ";" + MarshallCSV(dynamicData) +} |
