435 lines
9.5 KiB
Go
435 lines
9.5 KiB
Go
package guilddb
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
|
|
"dislate/internals/translator/lang"
|
|
_ "github.com/tursodatabase/go-libsql"
|
|
)
|
|
|
|
type SQLiteDB struct {
|
|
sql *sql.DB
|
|
}
|
|
|
|
func NewSQLiteDB(file string) (*SQLiteDB, error) {
|
|
db, err := sql.Open("libsql", file)
|
|
if err != nil {
|
|
return &SQLiteDB{}, err
|
|
}
|
|
return &SQLiteDB{db}, nil
|
|
}
|
|
|
|
func (db *SQLiteDB) Close() error {
|
|
return db.sql.Close()
|
|
}
|
|
|
|
func (db *SQLiteDB) Prepare() error {
|
|
_, err := db.sql.Exec(`
|
|
INSERT TABLE IF NOT EXISTS guild.messages (
|
|
ID text NOT NULL,
|
|
ChannelID text NOT NULL,
|
|
Language text NOT NULL,
|
|
OriginID text,
|
|
OriginChannelID text,
|
|
PRIMARY KEY(ID, ChannelID),
|
|
FOREIGN KEY(ChannelID) REFERENCES guild.channels(ID),
|
|
FOREIGN KEY(OriginalID) REFERENCES guild.messages(ID),
|
|
FOREIGN KEY(OriginalChannelID) REFERENCES guild.channels(ID)
|
|
);
|
|
`)
|
|
if err != nil {
|
|
return errors.Join(ErrInternal, err)
|
|
}
|
|
|
|
_, err = db.sql.Exec(`
|
|
INSERT TABLE IF NOT EXISTS guild-v1.channels (
|
|
ID text NOT NULL,
|
|
Language text NOT NULL,
|
|
PRIMARY KEY(ID)
|
|
);
|
|
INSERT TABLE IF NOT EXISTS guild-v1.channel-groups (
|
|
Channels text NOT NULL PRIMARY KEY
|
|
);
|
|
`)
|
|
if err != nil {
|
|
return errors.Join(ErrInternal, err)
|
|
}
|
|
|
|
_, err = db.sql.Exec(`
|
|
INSERT TABLE IF NOT EXISTS guild-v1.user-webhooks (
|
|
ID text NOT NULL,
|
|
ChannelID text NOT NULL,
|
|
UserID text NOT NULL,
|
|
Token text NOT NULL,
|
|
PRIMARY KEY(ID, ChannelID, UserID)
|
|
);
|
|
`)
|
|
if err != nil {
|
|
return errors.Join(ErrInternal, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (db *SQLiteDB) Message(channelID, messageID string) (Message, error) {
|
|
return db.selectMessage(`
|
|
SELECT * FROM guild-v1.messages
|
|
WHERE "ID" = $1 AND "ChannelID" = $2
|
|
`, messageID, channelID)
|
|
}
|
|
|
|
func (db *SQLiteDB) MessagesWithOrigin(originID, originChannelID string) ([]Message, error) {
|
|
return db.selectMessages(`
|
|
SELECT * FROM guild-v1.messages
|
|
WHERE "OriginID" = $1 AND "OriginChannelID" = $2
|
|
`, originID, originChannelID)
|
|
}
|
|
|
|
func (db *SQLiteDB) MessageWithOriginByLang(
|
|
originChannelID, originID string,
|
|
language lang.Language,
|
|
) (Message, error) {
|
|
return db.selectMessage(`
|
|
SELECT * FROM guild-v1.messages
|
|
WHERE "OriginID" = $1 AND "OriginChannelID" = $2 AND "Language" = $3
|
|
`, originID, originChannelID, language)
|
|
}
|
|
|
|
func (db *SQLiteDB) MessageInsert(m Message) error {
|
|
_, err := db.Channel(m.ChannelID)
|
|
if errors.Is(err, ErrNotFound) {
|
|
return errors.Join(
|
|
ErrPreconditionFailed,
|
|
fmt.Errorf("Channel %s doesn't exists in the database", m.ChannelID),
|
|
)
|
|
} else if err != nil {
|
|
return errors.Join(
|
|
ErrInternal,
|
|
errors.New("Failed to check if Channel exists in the database"),
|
|
err,
|
|
)
|
|
}
|
|
|
|
r, err := db.sql.Exec(`
|
|
INSERT INTO guild-v1.messages (ID, ChannelID, Language, OriginID, OriginChannelID)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
`, m.ID, m.ChannelID, m.Language, m.OriginID, m.OriginChannelID)
|
|
|
|
if err != nil {
|
|
return errors.Join(ErrInternal, err)
|
|
} else if rows, _ := r.RowsAffected(); rows == 0 {
|
|
return ErrNoAffect
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (db *SQLiteDB) MessageUpdate(message Message) error {
|
|
r, err := db.sql.Exec(`
|
|
UPDATE guild-v1.messages
|
|
SET Language = $1, OriginChannelID = $2, OriginID = $3
|
|
WHERE "ID" = $4 AND "ChannelID" = $5
|
|
`, message.Language,
|
|
message.OriginChannelID,
|
|
message.OriginID,
|
|
message.ID,
|
|
message.ChannelID,
|
|
)
|
|
|
|
if err != nil {
|
|
return errors.Join(ErrInternal, err)
|
|
} else if rows, _ := r.RowsAffected(); rows == 0 {
|
|
return ErrNoAffect
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (db *SQLiteDB) MessageDelete(message Message) error {
|
|
_, err := db.sql.Exec(`
|
|
DELETE guild-v1.channels
|
|
WHERE "OriginID" = $1 AND "OriginChannelID" = $2
|
|
`, message.ID, message.ChannelID)
|
|
|
|
if err != nil {
|
|
return errors.Join(ErrInternal, err)
|
|
}
|
|
|
|
r, err := db.sql.Exec(`
|
|
DELETE guild-v1.channels
|
|
WHERE "ID" = $1 AND "ChannelID" = $2
|
|
`, message.ID, message.ChannelID)
|
|
|
|
if err != nil {
|
|
return errors.Join(ErrInternal, err)
|
|
} else if rows, _ := r.RowsAffected(); rows == 0 {
|
|
return ErrNoAffect
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (db *SQLiteDB) selectMessage(query string, args ...any) (Message, error) {
|
|
var m Message
|
|
err := db.sql.QueryRow(query, args...).
|
|
Scan(&m.ID, &m.ChannelID, &m.Language, &m.OriginID, &m.OriginChannelID)
|
|
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return m, errors.Join(ErrNotFound, err)
|
|
} else if err != nil {
|
|
return m, errors.Join(ErrInternal, err)
|
|
}
|
|
|
|
return m, nil
|
|
}
|
|
|
|
func (db *SQLiteDB) selectMessages(query string, args ...any) ([]Message, error) {
|
|
r, err := db.sql.Query(query, args...)
|
|
|
|
if err != nil {
|
|
return []Message{}, errors.Join(ErrInternal, err)
|
|
}
|
|
|
|
var ms []Message
|
|
for r.Next() {
|
|
var m Message
|
|
|
|
err = r.Scan(&m.ID, &m.ChannelID, &m.Language, &m.OriginID, &m.OriginChannelID)
|
|
if err != nil {
|
|
return ms, errors.Join(
|
|
ErrInternal,
|
|
fmt.Errorf("Query: %s\nArguments: %v", query, args),
|
|
err,
|
|
)
|
|
}
|
|
|
|
ms = append(ms, m)
|
|
}
|
|
|
|
if len(ms) == 0 {
|
|
return ms, errors.Join(
|
|
ErrNotFound,
|
|
fmt.Errorf("Query: %s\nArguments: %v", query, args),
|
|
)
|
|
}
|
|
return ms, err
|
|
}
|
|
|
|
func (db *SQLiteDB) Channel(channelID string) (Channel, error) {
|
|
return db.selectChannel(`
|
|
SELECT (ID, Language) FROM guild-v1.channels
|
|
WHERE "ID" = $1
|
|
`, channelID)
|
|
}
|
|
|
|
func (db *SQLiteDB) ChannelInsert(c Channel) error {
|
|
r, err := db.sql.Exec(`
|
|
INSERT INTO guild-v1.channels (ID, Language)
|
|
VALUES ($1, $2)
|
|
`, c.ID, c.Language)
|
|
|
|
if err != nil {
|
|
return errors.Join(ErrInternal, err)
|
|
} else if rows, _ := r.RowsAffected(); rows == 0 {
|
|
return ErrNoAffect
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (db *SQLiteDB) ChannelUpdate(channel Channel) error {
|
|
r, err := db.sql.Exec(`
|
|
UPDATE guild-v1.channels
|
|
SET Language = $1
|
|
WHERE "ID" = $2
|
|
`, channel.Language, channel.ID)
|
|
|
|
if err != nil {
|
|
return errors.Join(ErrInternal, err)
|
|
} else if rows, _ := r.RowsAffected(); rows == 0 {
|
|
return ErrNoAffect
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (db *SQLiteDB) ChannelDelete(channel Channel) error {
|
|
r, err := db.sql.Exec(`
|
|
DELETE guild-v1.channels
|
|
WHERE "ID" = $1
|
|
`, channel.ID)
|
|
|
|
if err != nil {
|
|
return errors.Join(ErrInternal, err)
|
|
} else if rows, _ := r.RowsAffected(); rows == 0 {
|
|
return ErrNoAffect
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (db *SQLiteDB) ChannelGroup(channelID string) (ChannelGroup, error) {
|
|
var g string
|
|
|
|
err := db.sql.QueryRow(`
|
|
SELECT (ID, Language) FROM guild-v1.channels
|
|
WHERE "Channels" LIKE "%$1%"
|
|
`, channelID).Scan(&g)
|
|
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return ChannelGroup{}, errors.Join(ErrNotFound, err)
|
|
} else if err != nil {
|
|
return ChannelGroup{}, errors.Join(ErrInternal, err)
|
|
}
|
|
|
|
ids := strings.Split(g, ",")
|
|
if !slices.IsSorted(ids) {
|
|
return ChannelGroup{}, errors.Join(
|
|
ErrInvalidObject,
|
|
fmt.Errorf("Channel in database is invalid, ids are not sorted: %s", ids),
|
|
)
|
|
}
|
|
for i, v := range ids {
|
|
ids[i] = fmt.Sprintf("\"ID\" = %s", v)
|
|
}
|
|
|
|
cs, err := db.selectChannels(fmt.Sprintf(`
|
|
SELECT (ID, Language) FROM guild-v1.channels
|
|
WHERE %s
|
|
`, strings.Join(ids, " OR ")))
|
|
|
|
if errors.Is(err, ErrNotFound) || len(cs) != len(ids) {
|
|
return ChannelGroup{}, errors.Join(
|
|
ErrPreconditionFailed,
|
|
fmt.Errorf("ChannelGroup has Channels that doesn't exist in the database, group: %s", ids),
|
|
err,
|
|
)
|
|
} else if err != nil {
|
|
return ChannelGroup{}, errors.Join(ErrInternal, err)
|
|
}
|
|
|
|
return cs, nil
|
|
}
|
|
|
|
func (db *SQLiteDB) ChannelGroupInsert(group ChannelGroup) error {
|
|
var ids []string
|
|
for _, c := range group {
|
|
ids = append(ids, c.ID)
|
|
}
|
|
slices.Sort(ids)
|
|
|
|
r, err := db.sql.Exec(`
|
|
INSERT INTO guild-v1.channel-groups (Channels)
|
|
VALUES ($1)
|
|
`, strings.Join(ids, ","))
|
|
|
|
if err != nil {
|
|
return errors.Join(ErrInternal, err)
|
|
} else if rows, _ := r.RowsAffected(); rows == 0 {
|
|
return ErrNoAffect
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (db *SQLiteDB) ChannelGroupUpdate(group ChannelGroup) error {
|
|
var ids, idsq []string
|
|
for _, c := range group {
|
|
ids = append(ids, c.ID)
|
|
idsq = append(idsq, "\"ID\" LIKE \""+c.ID+"\"")
|
|
}
|
|
slices.Sort(ids)
|
|
|
|
r, err := db.sql.Exec(
|
|
fmt.Sprintf(`
|
|
UPDATE guild-v1.channel-groups
|
|
SET Channels = $1
|
|
WHERE %s
|
|
`, strings.Join(idsq, " OR ")),
|
|
strings.Join(ids, ","),
|
|
)
|
|
|
|
if err != nil {
|
|
return errors.Join(ErrInternal, err)
|
|
} else if rows, _ := r.RowsAffected(); rows == 0 {
|
|
return ErrNoAffect
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (db *SQLiteDB) ChannelGroupDelete(group ChannelGroup) error {
|
|
var ids, idsq []string
|
|
for _, c := range group {
|
|
ids = append(ids, c.ID)
|
|
idsq = append(idsq, "\"ID\" LIKE \""+c.ID+"\"")
|
|
}
|
|
slices.Sort(ids)
|
|
|
|
r, err := db.sql.Exec(
|
|
fmt.Sprintf(`
|
|
DELETE FROM guild-v1.channel-groups
|
|
WHERE %s
|
|
`, strings.Join(idsq, " OR ")),
|
|
strings.Join(ids, ","),
|
|
)
|
|
|
|
if err != nil {
|
|
return errors.Join(ErrInternal, err)
|
|
} else if rows, _ := r.RowsAffected(); rows == 0 {
|
|
return ErrNoAffect
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (db *SQLiteDB) selectChannel(query string, args ...any) (Channel, error) {
|
|
var c Channel
|
|
err := db.sql.QueryRow(query, args...).
|
|
Scan(&c.ID, &c.Language)
|
|
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return c, errors.Join(ErrNotFound, err)
|
|
} else if err != nil {
|
|
return c, errors.Join(ErrInternal, err)
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
func (db *SQLiteDB) selectChannels(query string, args ...any) ([]Channel, error) {
|
|
r, err := db.sql.Query(query, args...)
|
|
|
|
if err != nil {
|
|
return []Channel{}, errors.Join(ErrInternal, err)
|
|
}
|
|
|
|
var cs []Channel
|
|
for r.Next() {
|
|
var c Channel
|
|
|
|
err = r.Scan(&c.ID, &c.Language)
|
|
if err != nil {
|
|
return cs, errors.Join(
|
|
ErrInternal,
|
|
fmt.Errorf("Query: %s\nArguments: %v", query, args),
|
|
err,
|
|
)
|
|
}
|
|
|
|
cs = append(cs, c)
|
|
}
|
|
|
|
if len(cs) == 0 {
|
|
return cs, errors.Join(
|
|
ErrNotFound,
|
|
fmt.Errorf("Query: %s\nArguments: %v", query, args),
|
|
)
|
|
}
|
|
return cs, err
|
|
}
|