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

Cherrypick from master #1157

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@ _help_cache

.vscode/
!.vscode/settings.json
!.vscode/extensions.json
sealdice-lock.lock
3 changes: 3 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["golang.go", "task.vscode-task"]
}
36 changes: 36 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# https://taskfile.dev

version: '3'

tasks:
install:
cmds:
- go mod download
- go install github.com/golangci/golangci-lint/cmd/[email protected]
- go install golang.org/x/tools/cmd/goimports@latest
- go install github.com/pointlander/[email protected]
- go generate ./...

run:
deps: ['test-and-lint']
cmds:
- go run .
build:
deps: ['test-and-lint']
cmds:
- task: build-only

test-and-lint:
deps: ['test', 'lint']
test:
cmds:
- go test ./...
- go vet ./...
lint:
cmds:
- goimports -w .
- golangci-lint run

build-only:
cmds:
- go build .
8 changes: 8 additions & 0 deletions dice/builtin_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,14 @@ func (d *Dice) registerCoreCommands() {
return CmdExecuteResult{Matched: true, Solved: true}
}

if !cmdArgs.AmIBeMentioned {
// 裸指令,如果当前群内开启,予以提示
if ctx.IsCurGroupBotOn {
ReplyToSender(ctx, msg, "[退群指令] 请@我使用这个命令,以进行确认")
}
return CmdExecuteResult{Matched: true, Solved: true}
}

ReplyToSender(ctx, msg, DiceFormatTmpl(ctx, "核心:骰子退群预告"))

userName := ctx.Dice.Parent.TryGetUserName(msg.Sender.UserID)
Expand Down
124 changes: 75 additions & 49 deletions dice/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,74 +69,94 @@ type ConfigItem struct {
}

func (i *ConfigItem) UnmarshalJSON(data []byte) error {
raw := map[string]any{}
raw := map[string]json.RawMessage{}
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
var ok bool
if i.Key, ok = raw["key"].(string); !ok {
return errors.New("'key' must be a string")
if err := json.Unmarshal(raw["key"], &i.Key); err != nil {
return fmt.Errorf("ConfigItem: unmarshal 'key' failed as %w", err)
}
if i.Type, ok = raw["type"].(string); !ok {
return errors.New("'type' must be a string")
if err := json.Unmarshal(raw["type"], &i.Type); err != nil {
return fmt.Errorf("ConfigItem (%s): unmarshal 'type' failed as %w", i.Key, err)
}
if i.Description, ok = raw["description"].(string); !ok {
return errors.New("'description' must be a string")
if err := json.Unmarshal(raw["description"], &i.Description); err != nil {
return fmt.Errorf("ConfigItem (%s): unmarshal 'description' failed as %w", i.Key, err)
}
if v, ok := raw["deprecated"]; ok {
if i.Deprecated, ok = v.(bool); !ok {
return errors.New("'deprecated' must be a bool")
if err := json.Unmarshal(v, &i.Deprecated); err != nil {
return fmt.Errorf("ConfigItem (%s): unmarshal 'deprecated' failed as %w", i.Key, err)
}
}

switch i.Type {
case "string", "bool", "float":
i.DefaultValue = raw["defaultValue"]
i.Value = raw["value"]
case "string", "task:cron", "task:daily":
var stringVal string
if err := json.Unmarshal(raw["defaultValue"], &stringVal); err != nil {
return fmt.Errorf("ConfigItem (%s-%s): unmarshal 'defaultValue' failed as %w", i.Key, i.Type, err)
}
i.DefaultValue = stringVal
if err := json.Unmarshal(raw["value"], &stringVal); err != nil {
return fmt.Errorf("ConfigItem (%s-%s): unmarshal 'value' failed as %w", i.Key, i.Type, err)
}
i.Value = stringVal
case "bool":
var boolVal bool
if err := json.Unmarshal(raw["defaultValue"], &boolVal); err != nil {
return fmt.Errorf("ConfigItem (%s-%s): unmarshal 'defaultValue' failed as %w", i.Key, i.Type, err)
}
i.DefaultValue = boolVal
if err := json.Unmarshal(raw["value"], &boolVal); err != nil {
return fmt.Errorf("ConfigItem (%s-%s): unmarshal 'value' failed as %w", i.Key, i.Type, err)
}
i.Value = boolVal
case "float":
var floatVal float64
if err := json.Unmarshal(raw["defaultValue"], &floatVal); err != nil {
return fmt.Errorf("ConfigItem (%s-%s): unmarshal 'defaultValue' failed as %w", i.Key, i.Type, err)
}
i.DefaultValue = floatVal
if err := json.Unmarshal(raw["value"], &floatVal); err != nil {
return fmt.Errorf("ConfigItem (%s-%s): unmarshal 'value' failed as %w", i.Key, i.Type, err)
}
i.Value = floatVal
case "int":
// 2024.08.09 1.4.6首发版本unmarshal产生类型报错修复
if v, ok := raw["defaultValue"].(float64); ok {
i.DefaultValue = int64(v)
} else if v, ok := raw["defaultValue"].(int64); ok {
i.DefaultValue = v
}
if v, ok := raw["value"]; ok {
if v2, ok := v.(float64); ok {
i.Value = int64(v2)
} else if v2, ok := v.(int64); ok {
i.Value = v2
}
var intVal int64
if err := json.Unmarshal(raw["defaultValue"], &intVal); err != nil {
return fmt.Errorf("ConfigItem (%s-%s): unmarshal 'defaultValue' failed as %w", i.Key, i.Type, err)
}
i.DefaultValue = intVal
if err := json.Unmarshal(raw["value"], &intVal); err != nil {
return fmt.Errorf("ConfigItem (%s-%s): unmarshal 'value' failed as %w", i.Key, i.Type, err)
}
i.Value = intVal
case "template":
{
v := raw["defaultValue"].([]interface{})
strarr := make([]string, len(v))
for i, vv := range v {
strarr[i] = vv.(string)
}
i.DefaultValue = strarr
var templateVal []string
if err := json.Unmarshal(raw["defaultValue"], &templateVal); err != nil {
return fmt.Errorf("ConfigItem (%s-%s): unmarshal 'defaultValue' failed as %w", i.Key, i.Type, err)
}
if v, ok := raw["value"]; ok {
vv := v.([]interface{})
strarr := make([]string, len(vv))
for i, vv := range vv {
strarr[i] = vv.(string)
}
i.Value = strarr
i.DefaultValue = templateVal
if err := json.Unmarshal(raw["value"], &templateVal); err != nil {
return fmt.Errorf("ConfigItem (%s-%s): unmarshal 'value' failed as %w", i.Key, i.Type, err)
}
i.Value = templateVal
case "option":
i.DefaultValue = raw["defaultValue"]
i.Value = raw["value"]
v := raw["option"].([]interface{})
strarr := make([]string, len(v))
for i, vv := range v {
strarr[i] = vv.(string)
}
i.Option = strarr
var stringVal string
var optionVal []string
if err := json.Unmarshal(raw["defaultValue"], &stringVal); err != nil {
return fmt.Errorf("ConfigItem (%s-%s): unmarshal 'defaultValue' failed as %w", i.Key, i.Type, err)
}
i.DefaultValue = stringVal
if err := json.Unmarshal(raw["value"], &stringVal); err != nil {
return fmt.Errorf("ConfigItem (%s-%s): unmarshal 'value' failed as %w", i.Key, i.Type, err)
}
i.Value = stringVal
if err := json.Unmarshal(raw["option"], &optionVal); err != nil {
return fmt.Errorf("ConfigItem (%s-%s): unmarshal 'option' failed as %w", i.Key, i.Type, err)
}
i.Option = optionVal
default:
return errors.New("unsupported type " + i.Type)
return errors.New("ConfigItem.UnmarshalJSON: unsupported type " + i.Type)
}

return nil
}

Expand Down Expand Up @@ -982,6 +1002,9 @@ func setupBaseTextTemplate(d *Dice) {
"鸽子理由": guguReason,
},
"其它": {
"抽牌_牌堆列表": {
{"载入并开启的牌堆:\n{$t牌堆列表}", 1},
},
"抽牌_列表": {
{"{$t原始列表}", 1},
},
Expand Down Expand Up @@ -1675,6 +1698,9 @@ func setupBaseTextTemplate(d *Dice) {
},
},
"其它": {
"抽牌_牌堆列表": {
SubType: ".draw list",
},
"抽牌_列表": {
SubType: ".draw keys",
},
Expand Down
5 changes: 2 additions & 3 deletions dice/ext_coc7.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ func RegisterBuiltinExtCoc7(self *Dice) {
VarSetValueInt64(mctx, "$tD100", outcome)
VarSetValueInt64(mctx, "$t判定值", checkVal)
VarSetValueInt64(mctx, "$tSuccessRank", int64(successRank))
VarSetValueStr(mctx, "$t属性表达式文本", expr2Text)

var suffix string
var suffixFull string
Expand Down Expand Up @@ -447,12 +448,10 @@ func RegisterBuiltinExtCoc7(self *Dice) {
commandInfoItems = append(commandInfoItems, infoItem)

VarSetValueStr(mctx, "$t检定表达式文本", expr1Text)
VarSetValueStr(mctx, "$t属性表达式文本", expr2Text)
VarSetValueStr(mctx, "$t检定计算过程", detailWrap)
VarSetValueStr(mctx, "$t计算过程", detailWrap)

SetTempVars(mctx, mctx.Player.Name) // 信息里没有QQ昵称,用这个顶一下
VarSetValueStr(mctx, "$t结果文本", DiceFormatTmpl(mctx, "COC:检定_单项结果文本"))
return nil
}

Expand Down Expand Up @@ -1131,7 +1130,7 @@ func RegisterBuiltinExtCoc7(self *Dice) {
cmdTi := &CmdItemInfo{
Name: "ti",
ShortHelp: ".ti // 抽取一个临时性疯狂症状",
Help: "抽取临时性疯狂症状:\n.li // 抽取一个临时性疯狂症状",
Help: "抽取临时性疯狂症状:\n.ti // 抽取一个临时性疯狂症状",
Solve: func(ctx *MsgContext, msg *Message, cmdArgs *CmdArgs) CmdExecuteResult {
if cmdArgs.IsArgEqual(1, "help") {
return CmdExecuteResult{Matched: true, Solved: true, ShowHelp: true}
Expand Down
5 changes: 3 additions & 2 deletions dice/ext_deck.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ func RegisterBuiltinExtDeck(d *Dice) {
}

if strings.EqualFold(deckName, "list") { //nolint:nestif
text := "载入并开启的牌堆:\n"
text := ""
for _, i := range ctx.Dice.DeckList {
if i.Enable {
author := fmt.Sprintf(" 作者:%s", i.Author)
Expand All @@ -640,7 +640,8 @@ func RegisterBuiltinExtDeck(d *Dice) {
text += fmt.Sprintf("- %s 格式: %s%s%s 牌组数量: %d\n", i.Name, i.Format, author, version, count)
}
}
ReplyToSender(ctx, msg, text)
VarSetValueStr(ctx, "$t牌堆列表", text)
ReplyToSender(ctx, msg, DiceFormatTmpl(ctx, "其它:抽牌_牌堆列表"))
} else if strings.EqualFold(deckName, "desc") {
// 查看详情
text := cmdArgs.GetArgN(2)
Expand Down
2 changes: 1 addition & 1 deletion dice/ext_exp.go
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ func getCmdStBase() *CmdItemInfo {

// 进行简化卡的尝试解析
input := cmdArgs.CleanArgs
re := regexp.MustCompile(`^(([^\s\-#]{1,25})([-#]))([^\s\d]+\d+)`)
re := regexp.MustCompile(`^(([^\s\-#]{1,25})([-#]))([^=\s\d(\[{\-+]+\d+)`)
matches := re.FindStringSubmatch(input)
if len(matches) > 0 {
flag := matches[3]
Expand Down
3 changes: 2 additions & 1 deletion dice/ext_log.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,8 @@ func RegisterBuiltinExtLog(self *Dice) {
group.UpdatedAtTime = time.Now().Unix()

time.Sleep(time.Duration(0.3 * float64(time.Second)))
getAndUpload(group.GroupID, group.LogCurName)
// Note: 2024-10-15 经过简单测试,似乎能缓解#1034的问题,但无法根本解决。
go getAndUpload(group.GroupID, group.LogCurName)
group.LogCurName = ""
group.UpdatedAtTime = time.Now().Unix()
return CmdExecuteResult{Matched: true, Solved: true}
Expand Down
12 changes: 12 additions & 0 deletions dice/platform_adapter_gocq.go
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,18 @@ func (pa *PlatformAdapterGocq) Serve() int {
// {"group_id":564808710,"notice_type":"group_decrease","operator_id":2589922907,"post_type":"notice","self_id":2589922907,"sub_type":"leave","time":1651584460,"user_id":2589922907}
groupName := dm.TryGetGroupName(msg.GroupID)
txt := fmt.Sprintf("离开群组或群解散: <%s>(%s)", groupName, msgQQ.GroupID)
// 这个就是要删除的部分,离开这个群组=群组退出=删除对应的群聊绑定信息(也就是用户的骰子和这个群聊无关了)
// 同时考虑到:QQ群团队发布公告称,由于业务调整,“恢复QQ群”功能将于2023年10月13日起正式下线,届时涉及QQ群相关的恢复功能都将无法使用,可以安心删除群聊对应绑定信息。
group, exists := session.ServiceAtNew[msg.GroupID]
if !exists {
txtErr := fmt.Sprintf("离开群组或群解散,删除对应群聊信息失败: <%s>(%s)", groupName, msgQQ.GroupID)
log.Error(txtErr)
ctx.Notice(txtErr)
return
}
// TODO:存疑,根据DISMISS的代码复制而来
group.DiceIDExistsMap.Delete(ep.UserID)
group.UpdatedAtTime = time.Now().Unix()
log.Info(txt)
ctx.Notice(txt)
return
Expand Down
15 changes: 12 additions & 3 deletions dice/platform_adapter_gocq_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ func textAssetsConvert(s string) string {
}
cq := CQCommand{
Type: cqType,
Args: map[string]string{"file": u.String(), "cache": "0"},
Args: map[string]string{"file": EscapeComma(u.String()), "cache": "0"},
}
return cq.Compile()
}
Expand All @@ -682,7 +682,7 @@ func textAssetsConvert(s string) string {
}
cq := CQCommand{
Type: cqType,
Args: map[string]string{"file": u.String()},
Args: map[string]string{"file": EscapeComma(u.String())},
}
return cq.Compile()
}
Expand All @@ -701,6 +701,7 @@ func textAssetsConvert(s string) string {
return
}
if strings.HasPrefix(fn, "file://") || strings.HasPrefix(fn, "http://") || strings.HasPrefix(fn, "https://") || strings.HasPrefix(fn, "base64://") {
cq.Args["file"] = EscapeComma(cq.Args["file"])
return
}
if strings.HasSuffix(fn, ".image") && len(fn) == 32+6 {
Expand All @@ -727,7 +728,7 @@ func textAssetsConvert(s string) string {
Scheme: "file",
Path: filepath.ToSlash(afn),
}
cq.Args["file"] = u.String()
cq.Args["file"] = EscapeComma(u.String())
}
} else {
cq.Overwrite = "[CQ码读取的不是当前目录文件或临时文件,可能是恶意行为,已禁止]"
Expand All @@ -740,3 +741,11 @@ func textAssetsConvert(s string) string {
text = ImageRewrite(text, solve2)
return CQRewrite(text, solve)
}

func EscapeComma(text string) string {
// 逗号属于URL合法字符,故只对file协议格式进行处理
if strings.HasPrefix(text, "file://") {
return strings.ReplaceAll(text, ",", "%2C")
}
return text
}
Loading
Loading