diff --git a/common/cplogs.go b/common/cplogs.go deleted file mode 100644 index eced0530ac..0000000000 --- a/common/cplogs.go +++ /dev/null @@ -1,63 +0,0 @@ -package common - -import ( - "encoding/json" - "fmt" - "time" - - "github.com/jonas747/discordgo" - "github.com/mediocregopher/radix/v3" -) - -type CPLogEntry struct { - Timestamp int64 `json:"ts"` - Action string `json:"action"` - - TimestampString string `json:"-"` -} - -func AddCPLogEntry(user *discordgo.User, guild int64, args ...interface{}) { - action := fmt.Sprintf("(UserID: %d) %s#%s: %s", user.ID, user.Username, user.Discriminator, fmt.Sprint(args...)) - - now := time.Now() - entry := &CPLogEntry{ - Timestamp: now.Unix(), - Action: action, - } - - serialized, err := json.Marshal(entry) - if err != nil { - logger.WithError(err).Error("Failed marshalling cp log entry") - return - } - - key := "cp_logs:" + discordgo.StrID(guild) - err = RedisPool.Do(radix.Cmd(nil, "LPUSH", key, string(serialized))) - RedisPool.Do(radix.Cmd(nil, "LTRIM", key, "0", "100")) - if err != nil { - logger.WithError(err).WithField("guild", guild).Error("Failed updating cp logs") - } -} - -func GetCPLogEntries(guild int64) ([]*CPLogEntry, error) { - var entriesRaw [][]byte - err := RedisPool.Do(radix.Cmd(&entriesRaw, "LRANGE", "cp_logs:"+discordgo.StrID(guild), "0", "-1")) - if err != nil { - return nil, err - } - - result := make([]*CPLogEntry, len(entriesRaw)) - - for k, entryRaw := range entriesRaw { - var decoded *CPLogEntry - err = json.Unmarshal(entryRaw, &decoded) - if err != nil { - result[k] = &CPLogEntry{Action: "Failed decoding"} - logger.WithError(err).WithField("guild", guild).WithField("cp_log_enry", k).Error("Failed decoding cp log entry") - } else { - decoded.TimestampString = time.Unix(decoded.Timestamp, 0).Format(time.Stamp) - result[k] = decoded - } - } - return result, nil -} diff --git a/common/cplogs/cplogs.go b/common/cplogs/cplogs.go new file mode 100644 index 0000000000..3d2460b5de --- /dev/null +++ b/common/cplogs/cplogs.go @@ -0,0 +1,13 @@ +package cplogs + +type ActionFormat struct { + Key string + FormatString string +} + +var actionFormats = make(map[string]*ActionFormat) + +// RegisterActionFormat sets up a action format, call this in your package init function +func RegisterActionFormat(format *ActionFormat) { + actionFormats[format.Key] = format +} diff --git a/common/cplogs/models.go b/common/cplogs/models.go new file mode 100644 index 0000000000..e1a3361030 --- /dev/null +++ b/common/cplogs/models.go @@ -0,0 +1,172 @@ +package cplogs + +import ( + "fmt" + "time" +) + +const DBSchema = ` +CREATE TABLE IF NOT EXISTS panel_logs ( + guild_id BIGINT NOT NULL, + local_id BIGINT NOT NULL, + + author_id BIGINT NOT NULL, + author_username TEXT NOT NULL, + + action TEXT NOT NULL, + + param1_type SMALLINT NOT NULL, + param1_int BIGINT NOT NULL, + param1_string TEXT NOT NULL, + + param2_type SMALLINT NOT NULL, + param2_int BIGINT NOT NULL, + param2_string TEXT NOT NULL, + + created_at TIMESTAMP WITH TIME ZONE NOT NULL, + + PRIMARY KEY(guild_id, local_id) +) +` + +type rawLogEntry struct { + GuildID int64 `db:"guild_id"` + LocalID int64 `db:"local_id"` + + AuthorID int64 `db:"author_id"` + AuthorUsername string `db:"author_username"` + + Action string `db:"action"` + + Param1Type uint8 `db:"param1_type"` + Param1Int int64 `db:"param1_int"` + Param1String string `db:"param1_string"` + + Param2Type uint8 `db:"param2_type"` + Param2Int int64 `db:"param2_int"` + Param2String string `db:"param2_string"` + + CreatedAt time.Time `db:"created_at"` +} + +func (r *rawLogEntry) toLogEntry() *LogEntry { + format, ok := actionFormats[r.Action] + if !ok { + panic("unknown action format: " + r.Action) + } + + entry := &LogEntry{ + GuildID: r.GuildID, + LocalID: r.LocalID, + + AuthorID: r.AuthorID, + AuthorUsername: r.AuthorUsername, + Action: &LogAction{ + FormatStr: format.FormatString, + Key: r.Action, + }, + + CreatedAt: r.CreatedAt, + } + + if r.Param1Type > 0 { + entry.Action.Params = append(entry.Action.Params, readParam(r.Param1Type, r.Param1Int, r.Param1String)) + if r.Param2Type > 0 { + entry.Action.Params = append(entry.Action.Params, readParam(r.Param2Type, r.Param2Int, r.Param2String)) + } + } + + return entry +} + +func readParam(paramType uint8, paramInt int64, paramString string) *Param { + var value interface{} + switch ParamType(paramType) { + case ParamTypeInt: + value = paramInt + case ParamTypeString: + value = paramString + } + + return &Param{ + Type: ParamType(paramType), + Value: value, + } + +} + +type LogEntry struct { + GuildID int64 + LocalID int64 + + AuthorID int64 + AuthorUsername string + + Action *LogAction + + CreatedAt time.Time +} + +func (l *LogEntry) toRawLogEntry() *rawLogEntry { + rawEntry := &rawLogEntry{ + GuildID: l.GuildID, + LocalID: l.LocalID, + + AuthorID: l.AuthorID, + AuthorUsername: l.AuthorUsername, + + Action: l.Action.Key, + CreatedAt: l.CreatedAt, + } + + if len(l.Action.Params) > 0 { + rawEntry.Param1Type = uint8(l.Action.Params[0].Type) + switch t := l.Action.Params[0].Value.(type) { + case int64: + rawEntry.Param1Int = t + case string: + rawEntry.Param1String = t + } + + if len(l.Action.Params) > 1 { + rawEntry.Param2Type = uint8(l.Action.Params[1].Type) + switch t := l.Action.Params[1].Value.(type) { + case int64: + rawEntry.Param2Int = t + case string: + rawEntry.Param2String = t + } + } + } + + return rawEntry +} + +type LogAction struct { + Key string + FormatStr string + Params []*Param +} + +func (l *LogAction) String() string { + f := l.FormatStr + args := []interface{}{} + for _, v := range l.Params { + args = append(args, v) + } + + return fmt.Sprintf(f, args...) +} + +type ParamType uint8 + +const ( + ParamTypeNone ParamType = 0 + ParamTypeInt ParamType = 1 + ParamTypeString ParamType = 2 +) + +type Param struct { + Type ParamType + Value interface{} +}