Eingabe von Lagerorten
All checks were successful
release-tag / release-image (push) Successful in 2m9s
All checks were successful
release-tag / release-image (push) Successful in 2m9s
This commit is contained in:
25
README.md
25
README.md
@@ -105,6 +105,26 @@ Team/Admin kann den Status manuell setzen:
|
||||
## Lager Commands
|
||||
|
||||
Nur Admins können Lagerbestände verändern. Team/Admin kann Lager prüfen und anzeigen.
|
||||
Lagerorte werden separat verwaltet und bei den `ort`-Feldern per Autocomplete vorgeschlagen. Dadurch entstehen keine Tippfehler wie `Orison`, `orison` und `Orison ` als getrennte Lagerorte.
|
||||
|
||||
### `/lagerort_add`
|
||||
|
||||
Team/Admin kann interne Lagerorte anlegen. Diese Orte sind danach bei `/lager_add`, `/lager_remove` und `/lagerort_remove` als Auswahl verfügbar.
|
||||
|
||||
```text
|
||||
/lagerort_add name:Baijini Point
|
||||
/lagerort_add name:Orison
|
||||
```
|
||||
|
||||
### `/lagerort_remove`
|
||||
|
||||
Team/Admin kann Lagerorte löschen, aber nur wenn dort kein aktiver Bestand mehr liegt.
|
||||
|
||||
```text
|
||||
/lagerort_remove ort:Baijini Point
|
||||
```
|
||||
|
||||
Wenn noch Bestand vorhanden ist, lehnt der Bot das Löschen ab.
|
||||
|
||||
### `/lager_add`
|
||||
|
||||
@@ -115,7 +135,7 @@ Fügt Bestand hinzu oder setzt ihn absolut.
|
||||
/lager_add ware:Gold qualitaet:900 menge:100 einheit:SCU ort:Orison modus:setzen
|
||||
```
|
||||
|
||||
`ort` ist ein Pflichtfeld und bezeichnet den internen Lagerort. Er wird nur in Team-/Admin-Ausgaben, der internen Lagerprüfung und im Auditlog angezeigt. Öffentliche Auftragsposts und Endnutzer-DMs enthalten keine Lagerorte.
|
||||
`ort` ist ein Pflichtfeld und muss vorher mit `/lagerort_add` angelegt worden sein. Discord schlägt vorhandene Lagerorte beim Tippen automatisch vor. Der Ort wird nur in Team-/Admin-Ausgaben, der internen Lagerprüfung und im Auditlog angezeigt. Öffentliche Auftragsposts und Endnutzer-DMs enthalten keine Lagerorte.
|
||||
|
||||
Das Auditlog zeigt Bestandsänderungen mit altem und neuem Wert, z. B.:
|
||||
|
||||
@@ -131,7 +151,7 @@ Reduziert Bestand. Der Bot verhindert negative Bestände.
|
||||
/lager_remove ware:Gold qualitaet:900 menge:10 einheit:SCU ort:Orison
|
||||
```
|
||||
|
||||
Auch hier ist `ort` ein Pflichtfeld und bezieht sich auf genau diese Lagerposition. Dadurch werden Bestände an verschiedenen Orten sauber getrennt geführt.
|
||||
Auch hier ist `ort` ein Pflichtfeld und wird aus den angelegten Lagerorten vorgeschlagen. Dadurch werden Bestände an verschiedenen Orten sauber getrennt geführt.
|
||||
|
||||
### `/lager_liste`
|
||||
|
||||
@@ -164,6 +184,7 @@ Intern speichert der Bot weiterhin stabile technische Statuswerte wie `open`, `a
|
||||
|
||||
- `orders`: Aufträge und Discord-Message-Referenzen
|
||||
- `inventory`: interne Lagerpositionen
|
||||
- `storage_locations`: verwaltete Lagerorte für Autocomplete und Validierung
|
||||
- `order_events`: Audit-Log für Aufträge und Lageränderungen
|
||||
|
||||
SQLite wird mit WAL-Modus und `busy_timeout` betrieben, damit kleine produktive Deployments robuster laufen.
|
||||
|
||||
137
db.go
137
db.go
@@ -76,6 +76,17 @@ CREATE TABLE IF NOT EXISTS inventory (
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_inventory_lookup ON inventory(normalized_name, quality, quantity_unit);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS storage_locations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
normalized_name TEXT NOT NULL UNIQUE,
|
||||
created_by_id TEXT NOT NULL DEFAULT '',
|
||||
created_by_name TEXT NOT NULL DEFAULT '',
|
||||
created_at DATETIME NOT NULL,
|
||||
updated_at DATETIME NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_storage_locations_name ON storage_locations(normalized_name);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS order_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
order_id INTEGER,
|
||||
@@ -91,6 +102,12 @@ CREATE INDEX IF NOT EXISTS idx_order_events_order ON order_events(order_id);
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := s.db.Exec(`INSERT OR IGNORE INTO storage_locations(name, normalized_name, created_by_id, created_by_name, created_at, updated_at)
|
||||
SELECT DISTINCT TRIM(location), lower(TRIM(location)), 'migration', 'migration', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
|
||||
FROM inventory
|
||||
WHERE TRIM(location) <> ''`); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.ensureColumns()
|
||||
}
|
||||
|
||||
@@ -337,6 +354,16 @@ func (s *Store) UpsertInventory(item InventoryItem, delta bool) (*InventoryItem,
|
||||
item.Name = strings.TrimSpace(item.Name)
|
||||
item.Location = strings.TrimSpace(item.Location)
|
||||
item.NormalizedName = normalizeName(item.Name)
|
||||
if item.Location == "" {
|
||||
return nil, errors.New("lagerort ist ein pflichtfeld")
|
||||
}
|
||||
exists, err := s.StorageLocationExists(item.Location)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
return nil, errors.New("lagerort existiert nicht. Bitte zuerst mit /lagerort_add anlegen")
|
||||
}
|
||||
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
@@ -462,6 +489,116 @@ func (s *Store) FindInventoryForOrder(o *Order) ([]InventoryMatch, float64, erro
|
||||
return matches, total, rows.Err()
|
||||
}
|
||||
|
||||
func scanStorageLocation(scanner interface{ Scan(dest ...any) error }) (*StorageLocation, error) {
|
||||
loc := &StorageLocation{}
|
||||
if err := scanner.Scan(&loc.ID, &loc.Name, &loc.NormalizedName, &loc.CreatedByID, &loc.CreatedByName, &loc.CreatedAt, &loc.UpdatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return loc, nil
|
||||
}
|
||||
|
||||
func (s *Store) CreateStorageLocation(name, actorID, actorName string) (*StorageLocation, error) {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
return nil, errors.New("lagerort darf nicht leer sein")
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
normalized := normalizeName(name)
|
||||
_, err := s.db.Exec(`INSERT INTO storage_locations(name, normalized_name, created_by_id, created_by_name, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`, name, normalized, actorID, actorName, now, now)
|
||||
if err != nil {
|
||||
if strings.Contains(strings.ToLower(err.Error()), "unique") {
|
||||
return nil, errors.New("lagerort existiert bereits")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return s.GetStorageLocationByName(name)
|
||||
}
|
||||
|
||||
func (s *Store) GetStorageLocationByName(name string) (*StorageLocation, error) {
|
||||
row := s.db.QueryRow(`SELECT id, name, normalized_name, created_by_id, created_by_name, created_at, updated_at FROM storage_locations WHERE normalized_name=?`, normalizeName(name))
|
||||
return scanStorageLocation(row)
|
||||
}
|
||||
|
||||
func (s *Store) StorageLocationExists(name string) (bool, error) {
|
||||
if strings.TrimSpace(name) == "" {
|
||||
return false, nil
|
||||
}
|
||||
var exists int
|
||||
err := s.db.QueryRow(`SELECT 1 FROM storage_locations WHERE normalized_name=? LIMIT 1`, normalizeName(name)).Scan(&exists)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s *Store) DeleteStorageLocationIfEmpty(name string) (*StorageLocation, error) {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
return nil, errors.New("lagerort darf nicht leer sein")
|
||||
}
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
row := tx.QueryRow(`SELECT id, name, normalized_name, created_by_id, created_by_name, created_at, updated_at FROM storage_locations WHERE normalized_name=?`, normalizeName(name))
|
||||
loc, err := scanStorageLocation(row)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, errors.New("lagerort nicht gefunden")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var used int
|
||||
if err := tx.QueryRow(`SELECT COUNT(*) FROM inventory WHERE lower(TRIM(location))=? AND quantity_amount > 0`, loc.NormalizedName).Scan(&used); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if used > 0 {
|
||||
return nil, fmt.Errorf("lagerort ist nicht leer (%d aktive Lagerpositionen)", used)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`DELETE FROM storage_locations WHERE id=?`, loc.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return loc, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListStorageLocations(query string, limit int) ([]StorageLocation, error) {
|
||||
if limit <= 0 || limit > 25 {
|
||||
limit = 25
|
||||
}
|
||||
q := `SELECT id, name, normalized_name, created_by_id, created_by_name, created_at, updated_at FROM storage_locations`
|
||||
args := []any{}
|
||||
if strings.TrimSpace(query) != "" {
|
||||
q += ` WHERE normalized_name LIKE ?`
|
||||
args = append(args, "%"+normalizeName(query)+"%")
|
||||
}
|
||||
q += ` ORDER BY name COLLATE NOCASE LIMIT ?`
|
||||
args = append(args, limit)
|
||||
rows, err := s.db.Query(q, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var out []StorageLocation
|
||||
for rows.Next() {
|
||||
loc, err := scanStorageLocation(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, *loc)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func quantityString(amount float64, unit string) string {
|
||||
return fmt.Sprintf("%s %s", formatAmount(amount), unit)
|
||||
}
|
||||
|
||||
99
discord.go
99
discord.go
@@ -72,10 +72,12 @@ func (b *Bot) registerCommands() error {
|
||||
{Name: "auftrag_status_setzen", Description: "Admin/Team: setzt den Status eines Auftrags", Options: []*discordgo.ApplicationCommandOption{{Name: "id", Description: "Auftrags-ID", Type: discordgo.ApplicationCommandOptionInteger, Required: true}, {Name: "status", Description: "Neuer Status", Type: discordgo.ApplicationCommandOptionString, Required: true, Choices: statusChoices()}, {Name: "notiz", Description: "Interne Notiz", Type: discordgo.ApplicationCommandOptionString, Required: false}}},
|
||||
{Name: "auftrag_abbrechen", Description: "Bricht einen Auftrag ab", Options: []*discordgo.ApplicationCommandOption{{Name: "id", Description: "Auftrags-ID", Type: discordgo.ApplicationCommandOptionInteger, Required: true}, {Name: "grund", Description: "Optionaler Grund", Type: discordgo.ApplicationCommandOptionString, Required: false}}},
|
||||
{Name: "auftrag_nachricht", Description: "Sendet eine Nachricht an Einreicher und Annehmer", Options: []*discordgo.ApplicationCommandOption{{Name: "id", Description: "Auftrags-ID", Type: discordgo.ApplicationCommandOptionInteger, Required: true}, {Name: "text", Description: "Nachricht", Type: discordgo.ApplicationCommandOptionString, Required: true}}},
|
||||
{Name: "lager_add", Description: "Admin: Lagerbestand hinzufügen oder Bestand setzen", Options: []*discordgo.ApplicationCommandOption{{Name: "ware", Description: "Commodity oder Item", Type: discordgo.ApplicationCommandOptionString, Required: true}, {Name: "qualitaet", Description: "Qualität 0-1000", Type: discordgo.ApplicationCommandOptionInteger, Required: true, MinValue: ptrFloat(0), MaxValue: float64(1000)}, {Name: "menge", Description: "Menge als Zahl", Type: discordgo.ApplicationCommandOptionNumber, Required: true}, {Name: "einheit", Description: "SCU, cSCU oder Stück", Type: discordgo.ApplicationCommandOptionString, Required: true, Choices: unitChoices()}, {Name: "ort", Description: "Lagerort", Type: discordgo.ApplicationCommandOptionString, Required: true}, {Name: "notiz", Description: "Interne Notiz", Type: discordgo.ApplicationCommandOptionString, Required: false}, {Name: "modus", Description: "addiert oder setzt Bestand", Type: discordgo.ApplicationCommandOptionString, Required: false, Choices: []*discordgo.ApplicationCommandOptionChoice{{Name: "addieren", Value: "add"}, {Name: "setzen", Value: "set"}}}}},
|
||||
{Name: "lager_remove", Description: "Admin: Lagerbestand reduzieren", Options: []*discordgo.ApplicationCommandOption{{Name: "ware", Description: "Commodity oder Item", Type: discordgo.ApplicationCommandOptionString, Required: true}, {Name: "qualitaet", Description: "Qualität 0-1000", Type: discordgo.ApplicationCommandOptionInteger, Required: true, MinValue: ptrFloat(0), MaxValue: float64(1000)}, {Name: "menge", Description: "Menge als Zahl", Type: discordgo.ApplicationCommandOptionNumber, Required: true, MinValue: ptrFloat(0.0001)}, {Name: "einheit", Description: "SCU, cSCU oder Stück", Type: discordgo.ApplicationCommandOptionString, Required: true, Choices: unitChoices()}, {Name: "ort", Description: "Lagerort", Type: discordgo.ApplicationCommandOptionString, Required: true}, {Name: "notiz", Description: "Interne Notiz", Type: discordgo.ApplicationCommandOptionString, Required: false}}},
|
||||
{Name: "lager_add", Description: "Admin: Lagerbestand hinzufügen oder Bestand setzen", Options: []*discordgo.ApplicationCommandOption{{Name: "ware", Description: "Commodity oder Item", Type: discordgo.ApplicationCommandOptionString, Required: true}, {Name: "qualitaet", Description: "Qualität 0-1000", Type: discordgo.ApplicationCommandOptionInteger, Required: true, MinValue: ptrFloat(0), MaxValue: float64(1000)}, {Name: "menge", Description: "Menge als Zahl", Type: discordgo.ApplicationCommandOptionNumber, Required: true}, {Name: "einheit", Description: "SCU, cSCU oder Stück", Type: discordgo.ApplicationCommandOptionString, Required: true, Choices: unitChoices()}, {Name: "ort", Description: "Lagerort", Type: discordgo.ApplicationCommandOptionString, Required: true, Autocomplete: true}, {Name: "notiz", Description: "Interne Notiz", Type: discordgo.ApplicationCommandOptionString, Required: false}, {Name: "modus", Description: "addiert oder setzt Bestand", Type: discordgo.ApplicationCommandOptionString, Required: false, Choices: []*discordgo.ApplicationCommandOptionChoice{{Name: "addieren", Value: "add"}, {Name: "setzen", Value: "set"}}}}},
|
||||
{Name: "lager_remove", Description: "Admin: Lagerbestand reduzieren", Options: []*discordgo.ApplicationCommandOption{{Name: "ware", Description: "Commodity oder Item", Type: discordgo.ApplicationCommandOptionString, Required: true}, {Name: "qualitaet", Description: "Qualität 0-1000", Type: discordgo.ApplicationCommandOptionInteger, Required: true, MinValue: ptrFloat(0), MaxValue: float64(1000)}, {Name: "menge", Description: "Menge als Zahl", Type: discordgo.ApplicationCommandOptionNumber, Required: true, MinValue: ptrFloat(0.0001)}, {Name: "einheit", Description: "SCU, cSCU oder Stück", Type: discordgo.ApplicationCommandOptionString, Required: true, Choices: unitChoices()}, {Name: "ort", Description: "Lagerort", Type: discordgo.ApplicationCommandOptionString, Required: true, Autocomplete: true}, {Name: "notiz", Description: "Interne Notiz", Type: discordgo.ApplicationCommandOptionString, Required: false}}},
|
||||
{Name: "lager_liste", Description: "Admin/Team: zeigt internes Lager", Options: []*discordgo.ApplicationCommandOption{{Name: "suche", Description: "Ware oder Ort", Type: discordgo.ApplicationCommandOptionString, Required: false}, {Name: "limit", Description: "Maximal 50", Type: discordgo.ApplicationCommandOptionInteger, Required: false, MinValue: ptrFloat(1), MaxValue: float64(50)}}},
|
||||
{Name: "lager_check", Description: "Admin/Team: prüft Lagerbestand für Ware", Options: []*discordgo.ApplicationCommandOption{{Name: "ware", Description: "Commodity oder Item", Type: discordgo.ApplicationCommandOptionString, Required: true}, {Name: "qualitaet", Description: "Mindestqualität 0-1000", Type: discordgo.ApplicationCommandOptionInteger, Required: true, MinValue: ptrFloat(0), MaxValue: float64(1000)}, {Name: "menge", Description: "Benötigte Menge", Type: discordgo.ApplicationCommandOptionNumber, Required: true, MinValue: ptrFloat(0.0001)}, {Name: "einheit", Description: "SCU, cSCU oder Stück", Type: discordgo.ApplicationCommandOptionString, Required: true, Choices: unitChoices()}}},
|
||||
{Name: "lagerort_add", Description: "Admin/Team: legt einen Lagerort an", Options: []*discordgo.ApplicationCommandOption{{Name: "name", Description: "Name des Lagerorts", Type: discordgo.ApplicationCommandOptionString, Required: true}}},
|
||||
{Name: "lagerort_remove", Description: "Admin/Team: löscht einen leeren Lagerort", Options: []*discordgo.ApplicationCommandOption{{Name: "ort", Description: "Lagerort", Type: discordgo.ApplicationCommandOptionString, Required: true, Autocomplete: true}}},
|
||||
}
|
||||
for _, cmd := range commands {
|
||||
if _, err := b.dg.ApplicationCommandCreate(b.cfg.AppID, b.cfg.GuildID, cmd); err != nil {
|
||||
@@ -132,6 +134,8 @@ func (b *Bot) onInteraction(s *discordgo.Session, i *discordgo.InteractionCreate
|
||||
switch i.Type {
|
||||
case discordgo.InteractionApplicationCommand:
|
||||
b.handleCommand(s, i)
|
||||
case discordgo.InteractionApplicationCommandAutocomplete:
|
||||
b.handleAutocomplete(s, i)
|
||||
case discordgo.InteractionMessageComponent:
|
||||
b.handleButton(s, i)
|
||||
}
|
||||
@@ -168,6 +172,10 @@ func (b *Bot) handleCommand(s *discordgo.Session, i *discordgo.InteractionCreate
|
||||
b.cmdInventoryList(s, i)
|
||||
case "lager_check":
|
||||
b.cmdInventoryCheck(s, i)
|
||||
case "lagerort_add":
|
||||
b.cmdStorageLocationAdd(s, i)
|
||||
case "lagerort_remove":
|
||||
b.cmdStorageLocationRemove(s, i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,6 +404,15 @@ func (b *Bot) cmdInventoryAdd(s *discordgo.Session, i *discordgo.InteractionCrea
|
||||
b.replyEphemeral(s, i, "Lagerort ist ein Pflichtfeld.")
|
||||
return
|
||||
}
|
||||
exists, err := b.store.StorageLocationExists(item.Location)
|
||||
if err != nil {
|
||||
b.replyEphemeral(s, i, "Lagerort konnte nicht geprüft werden: "+err.Error())
|
||||
return
|
||||
}
|
||||
if !exists {
|
||||
b.replyEphemeral(s, i, "Lagerort existiert nicht. Bitte zuerst mit /lagerort_add anlegen.")
|
||||
return
|
||||
}
|
||||
oldAmount := 0.0
|
||||
if existing, err := b.store.GetInventoryByKey(normalizeName(item.Name), item.Quality, item.QuantityUnit, item.Location); err == nil {
|
||||
oldAmount = existing.QuantityAmount
|
||||
@@ -448,6 +465,84 @@ func (b *Bot) cmdInventoryCheck(s *discordgo.Session, i *discordgo.InteractionCr
|
||||
b.replyEphemeral(s, i, b.inventoryText(o))
|
||||
}
|
||||
|
||||
func (b *Bot) cmdStorageLocationAdd(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
if !b.isTeamOrAdmin(i) {
|
||||
b.replyEphemeral(s, i, "Nur Team/Admin kann Lagerorte anlegen.")
|
||||
return
|
||||
}
|
||||
name := strings.TrimSpace(optString(i, "name"))
|
||||
if name == "" {
|
||||
b.replyEphemeral(s, i, "Lagerort darf nicht leer sein.")
|
||||
return
|
||||
}
|
||||
loc, err := b.store.CreateStorageLocation(name, userID(i), userString(i))
|
||||
if err != nil {
|
||||
b.replyEphemeral(s, i, "Lagerort konnte nicht angelegt werden: "+err.Error())
|
||||
return
|
||||
}
|
||||
b.audit(fmt.Sprintf("Lagerort angelegt durch %s: %s", userString(i), loc.Name))
|
||||
b.replyEphemeral(s, i, fmt.Sprintf("Lagerort angelegt: %s", loc.Name))
|
||||
}
|
||||
|
||||
func (b *Bot) cmdStorageLocationRemove(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
if !b.isTeamOrAdmin(i) {
|
||||
b.replyEphemeral(s, i, "Nur Team/Admin kann Lagerorte löschen.")
|
||||
return
|
||||
}
|
||||
name := strings.TrimSpace(optString(i, "ort"))
|
||||
if name == "" {
|
||||
b.replyEphemeral(s, i, "Lagerort darf nicht leer sein.")
|
||||
return
|
||||
}
|
||||
loc, err := b.store.DeleteStorageLocationIfEmpty(name)
|
||||
if err != nil {
|
||||
b.replyEphemeral(s, i, "Lagerort konnte nicht gelöscht werden: "+err.Error())
|
||||
return
|
||||
}
|
||||
b.audit(fmt.Sprintf("Lagerort gelöscht durch %s: %s", userString(i), loc.Name))
|
||||
b.replyEphemeral(s, i, fmt.Sprintf("Lagerort gelöscht: %s", loc.Name))
|
||||
}
|
||||
|
||||
func (b *Bot) handleAutocomplete(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
cmd := i.ApplicationCommandData().Name
|
||||
focusedName, focusedValue := focusedOption(i)
|
||||
if focusedName != "ort" || (cmd != "lager_add" && cmd != "lager_remove" && cmd != "lagerort_remove") {
|
||||
b.respondAutocomplete(s, i, nil)
|
||||
return
|
||||
}
|
||||
if !b.isTeamOrAdmin(i) {
|
||||
b.respondAutocomplete(s, i, nil)
|
||||
return
|
||||
}
|
||||
locations, err := b.store.ListStorageLocations(focusedValue, 25)
|
||||
if err != nil {
|
||||
log.Println("location autocomplete failed:", err)
|
||||
b.respondAutocomplete(s, i, nil)
|
||||
return
|
||||
}
|
||||
choices := make([]*discordgo.ApplicationCommandOptionChoice, 0, len(locations))
|
||||
for _, loc := range locations {
|
||||
choices = append(choices, &discordgo.ApplicationCommandOptionChoice{Name: loc.Name, Value: loc.Name})
|
||||
}
|
||||
b.respondAutocomplete(s, i, choices)
|
||||
}
|
||||
|
||||
func (b *Bot) respondAutocomplete(s *discordgo.Session, i *discordgo.InteractionCreate, choices []*discordgo.ApplicationCommandOptionChoice) {
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionApplicationCommandAutocompleteResult,
|
||||
Data: &discordgo.InteractionResponseData{Choices: choices},
|
||||
})
|
||||
}
|
||||
|
||||
func focusedOption(i *discordgo.InteractionCreate) (string, string) {
|
||||
for _, opt := range i.ApplicationCommandData().Options {
|
||||
if opt.Focused {
|
||||
return opt.Name, opt.StringValue()
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func (b *Bot) handleButton(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
customID := i.MessageComponentData().CustomID
|
||||
parts := strings.Split(customID, ":")
|
||||
|
||||
Reference in New Issue
Block a user