mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-05-11 22:24:41 +00:00
83881e6b71
## Summary Auto-discovers previously-unknown hashtag channels by scanning decoded channel message text for `#name` mentions and surfacing them via `GetChannels`. Workflow (per the issue): 1. New channel message arrives on a known channel 2. Decoded text is scanned for `#hashtag` mentions 3. Any mention that doesn't match an existing channel is surfaced as a discovered channel (`discovered: true`, `messageCount: 0`) 4. Future traffic on that channel will populate the entry once it has its own packets ## Changes - `cmd/server/discovered_channels.go` — new file. `extractHashtagsFromText` parses `#name` mentions from free text, deduped, order-preserving. Trailing punctuation is excluded by the character class. - `cmd/server/store.go` — `GetChannels` now scans CHAN packet text for hashtags after building the primary channel map, and appends any unseen hashtag mentions as discovered entries. - `cmd/server/discovered_channels_test.go` — new tests covering parser edge cases (single, multi, dedup, punctuation, none, bare `#`) and end-to-end discovery via `GetChannels`. ## TDD - Red: `34f1817` — stub returns `nil`, both new tests fail on assertion (verified). - Green: `d27b3ed` — real implementation, full `cmd/server` test suite passes (21.7s). ## Notes - Discovered channels carry `messageCount: 0` and `lastActivity` set to the most recent mention's `firstSeen`, so they sort naturally alongside real channels. - Names are matched against existing entries by both `#name` and bare `name` so a channel that already has decoded traffic isn't double-listed. - The existing `channelsCache` (15s) covers the new code path; no separate invalidation needed since the source data (`byPayloadType[5]`) drives both maps. Fixes #688 --------- Co-authored-by: corescope-bot <bot@corescope.local>
86 lines
2.2 KiB
Go
86 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
// TestExtractHashtagsFromText covers the parsing helper used to discover new
|
|
// hashtag channels from decoded message text (issue #688).
|
|
func TestExtractHashtagsFromText(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
in string
|
|
want []string
|
|
}{
|
|
{
|
|
name: "single mention from issue body",
|
|
in: "Hey, I created new channel called #mesh, please join",
|
|
want: []string{"#mesh"},
|
|
},
|
|
{
|
|
name: "multiple mentions preserve order",
|
|
in: "join #mesh and #wardriving today",
|
|
want: []string{"#mesh", "#wardriving"},
|
|
},
|
|
{
|
|
name: "dedup repeated mentions",
|
|
in: "#x then #x again",
|
|
want: []string{"#x"},
|
|
},
|
|
{
|
|
name: "ignores trailing punctuation",
|
|
in: "check #fun!",
|
|
want: []string{"#fun"},
|
|
},
|
|
{
|
|
name: "no hashtag returns nil",
|
|
in: "nothing to see here",
|
|
want: nil,
|
|
},
|
|
{
|
|
name: "bare # is not a channel",
|
|
in: "issue #",
|
|
want: nil,
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got := extractHashtagsFromText(tc.in)
|
|
if !reflect.DeepEqual(got, tc.want) {
|
|
t.Fatalf("extractHashtagsFromText(%q): got %v, want %v", tc.in, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestGetChannels_DiscoversHashtagsFromMessages verifies that when a decoded
|
|
// CHAN message body mentions a previously-unknown hashtag channel, that
|
|
// channel is auto-registered in the GetChannels output (#688).
|
|
func TestGetChannels_DiscoversHashtagsFromMessages(t *testing.T) {
|
|
// One known channel (#general) where someone announces a new channel #mesh.
|
|
pkt := makeGrpTx(198, "general", "Alice: Hey, I created new channel called #mesh, please join", "Alice")
|
|
ps := newChannelTestStore([]*StoreTx{pkt})
|
|
|
|
channels := ps.GetChannels("")
|
|
|
|
var sawGeneral, sawMesh bool
|
|
for _, ch := range channels {
|
|
switch ch["name"] {
|
|
case "general":
|
|
sawGeneral = true
|
|
case "#mesh":
|
|
sawMesh = true
|
|
if d, _ := ch["discovered"].(bool); !d {
|
|
t.Errorf("expected discovered=true on #mesh, got %v", ch["discovered"])
|
|
}
|
|
}
|
|
}
|
|
if !sawGeneral {
|
|
t.Error("expected the source channel 'general' in GetChannels output")
|
|
}
|
|
if !sawMesh {
|
|
t.Errorf("expected discovered hashtag channel '#mesh' in GetChannels output; got %d channels: %+v", len(channels), channels)
|
|
}
|
|
}
|