diff --git a/config/config_telnet.go b/config/config_telnet.go index 5bbec3b..d257d38 100644 --- a/config/config_telnet.go +++ b/config/config_telnet.go @@ -9,6 +9,8 @@ import ( type Telnet struct { IsEnabled bool `toml:"enabled" desc:"Enable Telnet"` IsLegacy bool `toml:"legacy" desc:"EQEMU servers that run 0.8.0 versions need this set to true for item link support, everyone running any newer versions can leave it default (false)"` + LinkChunk1Size int `toml:"link_chunk1_size" desc:"Size of item links. Can leave at 0, will dynamically detect, Secrets custom is 9. but RoF2 is 6. Titanium is 6. Left for super custom servers."` + LinkChunk2Size int `toml:"link_chunk2_size" desc:"Size of item links. Can leave at 0, will dynamically detect, Secrets custom is 71. but RoF2 is 50. Titanium is 39. Left for super custom servers."` Host string `toml:"host" desc:"Address where telnet is found. By default, newer telnet clients will auto success on 127.0.0.1:9000"` Username string `toml:"username" desc:"Optional. Username to connect to telnet to. (By default, newer telnet clients will auto succeed if localhost)"` Password string `toml:"password" desc:"Optional. Password to connect to telnet to. (By default, newer telnet clients will auto succeed if localhost)"` diff --git a/telnet/telnet.go b/telnet/telnet.go index 4af0e4a..5477310 100644 --- a/telnet/telnet.go +++ b/telnet/telnet.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "regexp" "strings" "sync" "time" @@ -34,6 +35,7 @@ type Telnet struct { isPlayerDump bool lastPlayerDump time.Time characters map[string]*characterdb.Character + itemLinkCustom *regexp.Regexp } // New creates a new telnet connect @@ -61,6 +63,14 @@ func New(ctx context.Context, config config.Telnet) (*Telnet, error) { if config.Host == "" { config.Host = "127.0.0.1:23" } + if config.LinkChunk1Size > 0 && config.LinkChunk2Size > 0 { + var err error + t.itemLinkCustom, err = regexp.Compile(fmt.Sprintf(`\x12([0-9A-Z]{%d})[0-9A-Z]{%d}([\+0-9A-Za-z-'`+"`"+`:.,!?* ]+)\x12`, config.LinkChunk1Size, config.LinkChunk2Size)) + if err != nil { + return nil, fmt.Errorf("item link custom: %w", err) + } + + } return t, nil } diff --git a/telnet/telnet_msg.go b/telnet/telnet_msg.go index 19e457b..32c9247 100644 --- a/telnet/telnet_msg.go +++ b/telnet/telnet_msg.go @@ -14,16 +14,27 @@ import ( ) var ( - oldItemLink = regexp.MustCompile(`\x12([0-9A-Z]{6})[0-9A-Z]{39}([\+0-9A-Za-z-'` + "`" + `:.,!?* ]+)\x12`) - newItemLink = regexp.MustCompile(`\x12([0-9A-Z]{6})[0-9A-Z]{50}([\+0-9A-Za-z-'` + "`" + `:.,!?* ]+)\x12`) + // legacy item links in titanium is 6, then 39 bytes + itemLink39 = regexp.MustCompile(`\x12([0-9A-Z]{6})[0-9A-Z]{39}([\+0-9A-Za-z-'` + "`" + `:.,!?* ]+)\x12`) + // rof2+ item links are 6, then 50 bytes + itemLink50 = regexp.MustCompile(`\x12([0-9A-Z]{6})[0-9A-Z]{50}([\+0-9A-Za-z-'` + "`" + `:.,!?* ]+)\x12`) + // custom secrets itemlinks (64bit) are 9, then 71 bytes + itemLink71 = regexp.MustCompile(`\x12([0-9A-Z]{9})[0-9A-Z]{71}([\+0-9A-Za-z-'` + "`" + `:.,!?* ]+)\x12`) ) func (t *Telnet) convertLinks(message string) string { - matches := newItemLink.FindAllStringSubmatchIndex(message, -1) + matches := itemLink71.FindAllStringSubmatchIndex(message, -1) if len(matches) == 0 { - matches = oldItemLink.FindAllStringSubmatchIndex(message, -1) + matches = itemLink50.FindAllStringSubmatchIndex(message, -1) + if len(matches) == 0 { + matches = itemLink39.FindAllStringSubmatchIndex(message, -1) + } } + if t.itemLinkCustom != nil && len(matches) == 0 { + matches = t.itemLinkCustom.FindAllStringSubmatchIndex(message, -1) + } + out := message for _, submatches := range matches { if len(submatches) < 6 { @@ -31,7 +42,7 @@ func (t *Telnet) convertLinks(message string) string { } itemLink := message[submatches[2]:submatches[3]] - itemID, _ := strconv.ParseInt(itemLink, 16, 32) + itemID, _ := strconv.ParseInt(itemLink, 16, 64) //TODO: smarter debugging //if err != nil { diff --git a/telnet/telnet_msg_test.go b/telnet/telnet_msg_test.go index 4f8f3f1..2980431 100644 --- a/telnet/telnet_msg_test.go +++ b/telnet/telnet_msg_test.go @@ -22,47 +22,66 @@ func TestConvertLinks(t *testing.T) { } type test struct { - input, output string + name string + input string + output string } messages := []test{ { + name: "mask of tinkering x64", + input: "\x1200000046F00000000000000000000000000000000000000000000000000000000000000014D2720CMask of Tinkering\x12", + output: "http://test.com?itemid=1135 (Mask of Tinkering)", + }, { + name: "no url test", input: `no url test`, output: "no url test", }, { + name: "mask of tinkering", input: "\x1200046F000000000000000000000000000000000000000Mask of Tinkering\x12 0.8.0 style", output: "http://test.com?itemid=1135 (Mask of Tinkering) 0.8.0 style", }, { + name: "ring of prophetic visions", input: "\x1200F406000000000000000000000000000000000000000000B519D6B0Ring of Prophetic Visions\x12\n", output: "http://test.com?itemid=62470 (Ring of Prophetic Visions)", }, { + name: "mask of tinkering", input: "\x1200046F00000000000000000000000000000000000000000014D2720CMask of Tinkering\x12", output: "http://test.com?itemid=1135 (Mask of Tinkering)", }, { + name: "multiple link test", input: "multiple link test \x1200046F00000000000000000000000000000000000000000014D2720CMask of Tinkering\x12 and second \x1200046F00000000000000000000000000000000000000000014D2720CMask of Tinkering\x12", output: "multiple link test http://test.com?itemid=1135 (Mask of Tinkering) and second http://test.com?itemid=1135 (Mask of Tinkering)", }, { + name: "0.8.0 style double link", input: "\x1200046F000000000000000000000000000000000000000Mask of Tinkering\x12 0.8.0 style double link \x1200046F000000000000000000000000000000000000000Mask of Tinkering\x12", output: "http://test.com?itemid=1135 (Mask of Tinkering) 0.8.0 style double link http://test.com?itemid=1135 (Mask of Tinkering)", }, { + name: "0.8.0 style double link with asterisk", input: "\x1200046F000000000000000000000000000000000000000Mask of Tinkering**\x12 0.8.0 style double link \x1200046F000000000000000000000000000000000000000Mask of Tinkering**\x12", output: "http://test.com?itemid=1135 (Mask of Tinkering**) 0.8.0 style double link http://test.com?itemid=1135 (Mask of Tinkering**)", }, { + name: "forested gem", input: "\r> \b\bShin says ooc, '\x120112A4000000000000000000000000000000000000000000244AE3C6Frosted Gem of Ferocity\x12'\n", output: "> \u0008\u0008Shin says ooc, 'http://test.com?itemid=70308 (Frosted Gem of Ferocity)'", }, { + name: "tae ew war maul", input: "\r> \b\bShin says ooc, '\x120CA2150000000000000000000000000000000000000000002A46AF7CTae Ew War Maul**\x12'\n", output: "> \u0008\u0008Shin says ooc, 'http://test.com?itemid=827925 (Tae Ew War Maul**)'", }, { + name: "spell test", input: "\r> \b\bShin says ooc, '\x120CA2150000000000000000000000000000000000000000002A46AF7CSpell: Test\x12'\n", output: "> \u0008\u0008Shin says ooc, 'http://test.com?itemid=827925 (Spell: Test)'", }, { + name: "cestus test", input: "\r> \b\bShin says ooc, '\x1209756800000000000000000000000000000000000000000048F274D9Magmaband of Cestus Dei +1\x12'\n", output: "> \u0008\u0008Shin says ooc, 'http://test.com?itemid=619880 (Magmaband of Cestus Dei +1)'", }, { + name: "2 handed test1", input: "\r> \b\bShin says ooc, '\x1207A50C000000000000000000000000000000000000000000CC2F1766Infused 2 Handed Damage\x12'\n", output: "> \u0008\u0008Shin says ooc, 'http://test.com?itemid=501004 (Infused 2 Handed Damage)'", }, { + name: "2 handed test2", input: "\r> \b\bShin says ooc, '\x1207A50C000000000000000000000000000000000000000000CC2F1766Infused 2 Handed Damage\x12'\n", output: "> \u0008\u0008Shin says ooc, 'http://test.com?itemid=501004 (Infused 2 Handed Damage)'", // }, { @@ -73,6 +92,43 @@ func TestConvertLinks(t *testing.T) { // output: "> \u0008\u0008Shin says ooc, 'http://test.com?itemid=62470 (Spell: Wild Cat V (Tier 9)", }, } + for _, message := range messages { + result := client.convertLinks(message.input) + if result != message.output { + t.Fatalf("convertLinks %s failed: got %s, wanted %s", message.name, result, message.output) + } + } +} + +func TestConvert64Links(t *testing.T) { + //[\x1200046F000000000000000000000000000000000000000Mask of Tinkering\x12] + //latest looks like this + //\x1200F406000000000000000000000000000000000000000000B519D6B0Ring of Prophetic Visions\x12'\n" + client, err := New(context.Background(), config.Telnet{ + ItemURL: "http://test.com?itemid=", + }) + if err != nil { + t.Fatalf("new client: %s", err) + } + + type test struct { + input, output string + } + + messages := []test{ + {input: `no url test`, output: "no url test"}, + {input: "\x1200046F00000000000000000000000000000000000000000000000000Mask of Tinkering\x12 0.8.0 style", output: "http://test.com?itemid=1135 (Mask of Tinkering) 0.8.0 style"}, + {input: "\x1200087E000000000000000000000000000000000000000000B519D6B0Ring of Prophetic Visions\x12\n", output: "http://test.com?itemid=62470 (Ring of Prophetic Visions)"}, + {input: "\x1200046F00000000000000000000000000000000000000000014D2720CMask of Tinkering\x12", output: "http://test.com?itemid=1135 (Mask of Tinkering)"}, + {input: "multiple link test \x1200046F00000000000000000000000000000000000000000014D2720CMask of Tinkering\x12 and second \x1200046F00000000000000000000000000000000000000000014D2720CMask of Tinkering\x12", output: "multiple link test http://test.com?itemid=1135 (Mask of Tinkering) and second http://test.com?itemid=1135 (Mask of Tinkering)"}, + {input: "\x1200046F000000000000000000000000000000000000000Mask of Tinkering\x12 0.8.0 style double link \x1200046F000000000000000000000000000000000000000Mask of Tinkering\x12", output: "http://test.com?itemid=1135 (Mask of Tinkering) 0.8.0 style double link http://test.com?itemid=1135 (Mask of Tinkering)"}, + {input: "\x1200046F000000000000000000000000000000000000000Mask of Tinkering**\x12 0.8.0 style double link \x1200046F000000000000000000000000000000000000000Mask of Tinkering**\x12", output: "http://test.com?itemid=1135 (Mask of Tinkering**) 0.8.0 style double link http://test.com?itemid=1135 (Mask of Tinkering**)"}, + {input: "\r> \b\bShin says ooc, '\x120112A4000000000000000000000000000000000000000000244AE3C6Frosted Gem of Ferocity\x12'\n", output: "> \u0008\u0008Shin says ooc, 'http://test.com?itemid=70308 (Frosted Gem of Ferocity)'"}, + {input: "\r> \b\bShin says ooc, '\x120CA2150000000000000000000000000000000000000000002A46AF7CTae Ew War Maul**\x12'\n", output: "> \u0008\u0008Shin says ooc, 'http://test.com?itemid=827925 (Tae Ew War Maul**)'"}, + {input: "\r> \b\bShin says ooc, '\x120CA2150000000000000000000000000000000000000000002A46AF7CSpell: Test\x12'\n", output: "> \u0008\u0008Shin says ooc, 'http://test.com?itemid=827925 (Spell: Test)'"}, + {input: "\r> \b\bShin says ooc, '\x1209756800000000000000000000000000000000000000000048F274D9Magmaband of Cestus Dei +1\x12'\n", output: "> \u0008\u0008Shin says ooc, 'http://test.com?itemid=619880 (Magmaband of Cestus Dei +1)'"}, + {input: "\r> \b\bShin says ooc, '\x1207A50C000000000000000000000000000000000000000000CC2F1766Infused 2 Handed Damage\x12'\n", output: "> \u0008\u0008Shin says ooc, 'http://test.com?itemid=501004 (Infused 2 Handed Damage)'"}, + } for _, message := range messages { result := client.convertLinks(message.input) if result != message.output {