Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat mark color #5

Merged
merged 2 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Albion Onlineのギルド [Dog The Boston](https://twitter.com/DogTheBoston) 用
> - `/route-add` アバロンのルートを追加します。
> - `/route-print` アバロンのルートを画像で投稿します。
> - `/route-clear` アバロンのルートをリセットします。
> - `/route-mark` マップをマークします(色が変わります)
> - `/route-mark` マップの色を変えたりメモを載せたりします
> ## リアクション
> - `リアクション集計` 集計したいメッセージにリアクション(🤖)を行うとリマインダーを投稿します(2分後に自動削除)。
> ## おまけ
Expand All @@ -54,6 +54,9 @@ Albion Onlineのギルド [Dog The Boston](https://twitter.com/DogTheBoston) 用
- 機能: 会話(?)
- 内部: マルチインスタンス対応({リアクション|メンション}ハンドラのGuild ID対応)
- 改善: `ルートナビ` エラーメッセージをembedでキレイに出力する
- xxxx-xx-xx v1.6.0
- 機能: `ルートナビ` 色指定とコメントを追加
- 改善: `ルートナビ` ルートが多いときに画像生成に失敗する問題(タイムアウト値を増やした)
- 2023-12-05 v1.5.0
- 改善: `ルートナビ` ルートが多くなると画像が横長になるので、ルートが32個以上あるときの描画形式を変更した
- 修正: `ルートナビ` バージョンがあがるとセーブデータが引き継がれないバグ
Expand Down
20 changes: 18 additions & 2 deletions cmd/roanav/main.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
package main

import (
"encoding/json"
"fmt"
"time"

"github.com/ebiiim/conbukun/pkg/ao/data"
"github.com/ebiiim/conbukun/pkg/ao/roanav"
)

func mustEncodeMarkedMaps(mm []roanav.MarkedMap) string {
b, err := json.Marshal(mm)
if err != nil {
panic(err)
}
return string(b)
}

func mustGetMD(name string) data.MapData {
md, ok := data.GetMapDataFromName(name)
if !ok {
Expand Down Expand Up @@ -49,7 +58,7 @@ func nav1() *roanav.Navigation {
roanav.NewPortal(mdCA.ID, mdSO.ID, roanav.PortalTypeYellow, time.Now().Add(100*time.Minute), map[string]string{roanav.PortalDataKeyUser: "user1"}),
},
Data: map[string]string{
roanav.NavigationDataHideouts: fmt.Sprintf("%s,", mdQSV.ID),
roanav.NavigationDataMarkedMaps: mustEncodeMarkedMaps([]roanav.MarkedMap{}),
},
}
n.DeleteExpiredPortals()
Expand Down Expand Up @@ -115,7 +124,14 @@ func nav2() *roanav.Navigation {
roanav.NewPortal("TNL-077", "TNL-092", roanav.PortalTypeBlue, time.Now().Add(mustParseDuration("5h31m")), map[string]string{roanav.PortalDataKeyUser: usr}),
},
Data: map[string]string{
roanav.NavigationDataHideouts: "TNL-367",
roanav.NavigationDataMarkedMaps: mustEncodeMarkedMaps([]roanav.MarkedMap{
{ID: "TNL-149", Color: roanav.MarkedMapColorNone, Comment: "Just a comment"},
{ID: "TNL-398", Color: roanav.MarkedMapColorGreen, Comment: "Hoge's Hideout"},
{ID: "TNL-367", Color: roanav.MarkedMapColorPink, Comment: "Fuga's Hideout"},
{ID: "TNL-357", Color: roanav.MarkedMapColorPurple, Comment: "Purple!"},
{ID: "TNL-330", Color: roanav.MarkedMapColorOrange, Comment: "Orange!"},
{ID: "TNL-318", Color: roanav.MarkedMapColorBrown, Comment: "Brown!"},
}),
},
}
n.DeleteExpiredPortals()
Expand Down
24 changes: 24 additions & 0 deletions pkg/ao/roanav/navigation.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,15 @@ func NewPortal(map1, map2, typ string, expiredAt time.Time, data map[string]stri
}

const (
// NavigationDataHideouts is the key for the hideouts data.
// The value must be a comma-separated list of map IDs.
//
// Deprecated: use NavigationDataMarkedMaps instead.
NavigationDataHideouts = "hideouts"

// NavigationDataMarkedMaps is the key for the marked maps data.
// The value must be a JSON-encoded list of MarkedMap.
NavigationDataMarkedMaps = "marked"
)

type Navigation struct {
Expand Down Expand Up @@ -97,3 +105,19 @@ func (n *Navigation) DeleteExpiredPortals() {
}
n.Portals = newPortals
}

// MarkedMap holds the data of a marked map.
type MarkedMap struct {
ID string `json:"id"`
Color string `json:"color"`
Comment string `json:"comment"`
}

const (
MarkedMapColorNone = "none"
MarkedMapColorGreen = "green"
MarkedMapColorPink = "pink"
MarkedMapColorPurple = "purple"
MarkedMapColorOrange = "orange"
MarkedMapColorBrown = "brown"
)
48 changes: 41 additions & 7 deletions pkg/ao/roanav/plantuml.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package roanav

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"strings"
Expand Down Expand Up @@ -34,7 +35,7 @@ var _ Painter = (*KrokiPlantUMLPNGPainter)(nil)

const (
DefaultKrokiEndpoint = "https://kroki.io"
DefaultKrokiTimeout = 10 * time.Second
DefaultKrokiTimeout = 60 * time.Second // kroki.io is so slow :(
)

func NewKrokiPlantUMLPNGPainter(endpoint string, timeout time.Duration, mapData map[string]data.MapData, style string) *KrokiPlantUMLPNGPainter {
Expand Down Expand Up @@ -217,14 +218,47 @@ func (p *KrokiPlantUMLPNGPainter) toTemplateDataAgent(portalID string, navigatio
textColor = "black"
}

// check hideout
if v, ok := navigationData[NavigationDataHideouts]; ok {
for _, hideout := range strings.Split(v, ",") {
if hideout == portalID {
fillColor = "darkgreen"
textColor = "white"
// check marked maps
if v, ok := navigationData[NavigationDataMarkedMaps]; ok {
var markedMaps []MarkedMap
if err := json.Unmarshal([]byte(v), &markedMaps); err != nil {
return d, fmt.Errorf("json.Unmarshal: %w", err)
}
for _, m := range markedMaps {

if m.ID == portalID { // found!

// 1. add comment
d.Name = fmt.Sprintf("%s\\n%s", d.Name, m.Comment)

// 2. change color
switch m.Color {
case MarkedMapColorNone:
// do nothing; herited from above
case MarkedMapColorGreen:
fillColor = "darkgreen"
textColor = "white"
case MarkedMapColorPink:
fillColor = "deeppink"
textColor = "white"
case MarkedMapColorPurple:
fillColor = "darkviolet"
textColor = "white"
case MarkedMapColorOrange:
fillColor = "orange"
textColor = "black"
case MarkedMapColorBrown:
fillColor = "saddlebrown"
textColor = "white"
default:
// do nothing (normally unreachable)
}

// 3. stop searching
break

}

}
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/handlers/slashcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func HandleCmdHelp(s *discordgo.Session, i *discordgo.InteractionCreate) {
"- `/route-add` アバロンのルートを追加します。\n" +
"- `/route-print` アバロンのルートを画像で投稿します。\n" +
"- `/route-clear` アバロンのルートをリセットします。\n" +
"- `/route-mark` マップをマークします(色が変わります)。\n" +
"- `/route-mark` マップの色を変えたりメモを載せたりします。\n" +
"## リアクション\n" +
"- **リアクション集計** 集計したいメッセージにリアクション(" + emojis2msg(guildEmojis, EmojisReactionAddReactionRequired) + ")を行うとリマインダーを投稿します(2分後に自動削除)。\n" +
// "- [試験運用中] **リアクション集計(表)** 集計したいメッセージにリアクション(" + emojis2msg(guildEmojis, emojisReactionAddReactionStats) + ")を行うと表形式で投稿します(2分後に削除)。\n" +
Expand Down
148 changes: 130 additions & 18 deletions pkg/handlers/slashcmd_roanav.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ func (h *ROANavHandler) HandleCmdRouteAddCommand(s *discordgo.Session, i *discor
}
return
}
lg = lg.With().Str("navName", navName).Logger()

// Get the Navigation.
nav := h.GetOrCreateNavigation(navName)
Expand Down Expand Up @@ -316,6 +317,7 @@ func (h *ROANavHandler) HandleCmdRoutePrint(s *discordgo.Session, i *discordgo.I
}
return
}
lg = lg.With().Str("navName", navName).Logger()

// Get the Navigation.
nav := h.GetOrCreateNavigation(navName)
Expand Down Expand Up @@ -472,6 +474,33 @@ func (h *ROANavHandler) HandleCmdRouteMarkAutocomplete(s *discordgo.Session, i *
}
}

// upsertOrRemoveMarkedMap upserts or removes the marked map.
// If markedMap.Color is none and markedMap.Comment is empty, the element with same markedMap.ID will be removed.
func upsertOrRemoveMarkedMap(markedMaps []roanav.MarkedMap, markedMap roanav.MarkedMap) []roanav.MarkedMap {
if markedMap.ID == "" {
return markedMaps
}
var m2 []roanav.MarkedMap
isNew := true
for _, m := range markedMaps {
if m.ID != markedMap.ID {
m2 = append(m2, m)
} else {
isNew = false
if markedMap.Color == roanav.MarkedMapColorNone && markedMap.Comment == "" {
// remove
} else {
// update
m2 = append(m2, markedMap)
}
}
}
if isNew && !(markedMap.Color == roanav.MarkedMapColorNone && markedMap.Comment == "") {
m2 = append(m2, markedMap)
}
return m2
}

func (h *ROANavHandler) HandleCmdRouteMarkCommand(s *discordgo.Session, i *discordgo.InteractionCreate) {
lg := lg.With().Str(lkCmd, CmdRouteMark).Str(lkIID, i.ID).Logger()

Expand All @@ -484,6 +513,7 @@ func (h *ROANavHandler) HandleCmdRouteMarkCommand(s *discordgo.Session, i *disco
}
return
}
lg = lg.With().Str("navName", navName).Logger()

// Get the Navigation.
nav := h.GetOrCreateNavigation(navName)
Expand All @@ -492,33 +522,76 @@ func (h *ROANavHandler) HandleCmdRouteMarkCommand(s *discordgo.Session, i *disco
// Get arguments.
optMap := i.ApplicationCommandData().Options[0]
targetMap := optMap.StringValue()
lg.Info().Str("targetMap", targetMap).Msg("arguments")

optColor := i.ApplicationCommandData().Options[1]
targetColor := optColor.StringValue()

optComment := i.ApplicationCommandData().Options[2]
targetComment := optComment.StringValue()

lg.Info().Str("map", targetMap).Str("color", targetColor).Str("comment", targetComment).Msg("arguments")

// Validate arguments.
// nothing to validate

// Init marked maps if not exists.
if _, ok := nav.Data[roanav.NavigationDataHideouts]; !ok {
nav.Data[roanav.NavigationDataHideouts] = ""
// Construct MarkedMap.
markedMap := roanav.MarkedMap{
ID: targetMap,
Color: targetColor,
Comment: targetComment,
}

// Get current marked maps.
markedMaps := strings.Split(nav.Data[roanav.NavigationDataHideouts], ",")
// Init MarkedMaps if not exists.
if _, ok := nav.Data[roanav.NavigationDataMarkedMaps]; !ok {
nav.Data[roanav.NavigationDataMarkedMaps] = "[]"
}

// Toggle.
var newMarkedMaps []string
for _, m := range markedMaps {
if m == targetMap {
continue
// Get current marked maps.
var markedMaps []roanav.MarkedMap
if err := json.Unmarshal([]byte(nav.Data[roanav.NavigationDataMarkedMaps]), &markedMaps); err != nil {
lg.Error().Err(err).Msg("could not unmarshal marked maps")
if mErr := respondEphemeralMessage(s, i, fmt.Sprintf("エラー: マークされているマップの取得に失敗したわん。何回も発生する場合は管理者に知らせてほしいわん。 ```\n%v```", err)); mErr != nil {
lg.Error().Err(mErr).Msg("could not send InteractionResponse")
}
newMarkedMaps = append(newMarkedMaps, m)
return
}
if len(newMarkedMaps) == len(markedMaps) {
newMarkedMaps = append(newMarkedMaps, targetMap)

markedMaps = upsertOrRemoveMarkedMap(markedMaps, markedMap)

// Migrate old data.
// TODO: remove this migration code in the next version (v1.7?).
if _, ok := nav.Data[roanav.NavigationDataHideouts]; ok {
// Get old marked maps.
oldMarkedMaps := strings.Split(nav.Data[roanav.NavigationDataHideouts], ",")
// Migration: add as green marked map if not exists.
for _, om := range oldMarkedMaps {
var exists bool
for _, m := range markedMaps {
exists = exists || (m.ID == om)
}
if !exists {
markedMaps = upsertOrRemoveMarkedMap(markedMaps, roanav.MarkedMap{
ID: om,
Color: roanav.MarkedMapColorGreen,
Comment: "Hideout",
})
}
}
// Remove deprecated data.
delete(nav.Data, roanav.NavigationDataHideouts)
lg.Info().Msg("migrated old hideouts data")
}

// Set.
nav.Data[roanav.NavigationDataHideouts] = strings.Join(newMarkedMaps, ",")
// Set new marked maps.
markedMapsJSON, err := json.Marshal(markedMaps)
if err != nil {
lg.Error().Err(err).Msg("could not marshal marked maps")
if mErr := respondEphemeralMessage(s, i, fmt.Sprintf("エラー: マークされているマップの保存に失敗したわん。何回も発生する場合は管理者に知らせてほしいわん。 ```\n%v```", err)); mErr != nil {
lg.Error().Err(mErr).Msg("could not send InteractionResponse")
}
return
}
nav.Data[roanav.NavigationDataMarkedMaps] = string(markedMapsJSON)

// Save.
if err := h.Save(); err != nil {
Expand All @@ -527,8 +600,8 @@ func (h *ROANavHandler) HandleCmdRouteMarkCommand(s *discordgo.Session, i *disco

// String representation.
markedMapsStr := ""
for _, m := range newMarkedMaps {
md, ok := data.Maps[m]
for _, m := range markedMaps {
md, ok := data.Maps[m.ID]
if !ok {
continue
}
Expand Down Expand Up @@ -616,6 +689,45 @@ func (*ROANavHandler) CommandRouteMark() *discordgo.ApplicationCommand {
Autocomplete: true,
Required: true,
},
{
Name: "color",
Description: "なに色にする?(color=None かつ commentなし でマーク削除)",
Type: discordgo.ApplicationCommandOptionString,
Choices: []*discordgo.ApplicationCommandOptionChoice{
{
Name: "None",
Value: roanav.MarkedMapColorNone,
},
{
Name: "Green",
Value: roanav.MarkedMapColorGreen,
},
{
Name: "Pink",
Value: roanav.MarkedMapColorPink,
},
{
Name: "Purple",
Value: roanav.MarkedMapColorPurple,
},
{
Name: "Orange",
Value: roanav.MarkedMapColorOrange,
},
{
Name: "Brown",
Value: roanav.MarkedMapColorBrown,
},
},
Required: true,
},
{
Name: "comment",
Description: "マップ名の下に表示するコメント(任意)",
Type: discordgo.ApplicationCommandOptionString,
Required: false,
MaxLength: 20,
},
},
}
}
Loading