@@ -4,17 +4,95 @@ import (
44 "encoding/json"
55 "fmt"
66 "io/ioutil"
7+ "log"
78 "net/http"
89 "regexp"
10+ "time"
911
1012 parser "github.com/MemeLabs/chat-parser"
13+ "mvdan.cc/xurls/v2"
1114)
1215
13- func extractEntities (parserCtx * parser.ParserContext , urls * regexp.Regexp , msg string ) * Entities {
16+ var entities * EntityExtractor
17+
18+ func init () {
19+ var err error
20+ entities , err = NewEntityExtractor ()
21+ if err != nil {
22+ log .Fatal (err )
23+ }
24+
25+ go entities .scheduleEmoteSync ()
26+ }
27+
28+ func loadEmoteManifest () ([]string , error ) {
29+ resp , err := http .Get ("https://chat.strims.gg/emote-manifest.json" )
30+ if err != nil {
31+ return nil , fmt .Errorf ("failed to get emotes: %w" , err )
32+ }
33+ defer resp .Body .Close ()
34+ manifest := struct {
35+ Emotes []struct {
36+ Name string `json:"name"`
37+ } `json:"emotes"`
38+ }{}
39+ if err := json .NewDecoder (resp .Body ).Decode (& manifest ); err != nil {
40+ return nil , fmt .Errorf ("failed to parse emotes manifest: %w" , err )
41+ }
42+
43+ emotes := make ([]string , len (manifest .Emotes ))
44+ for i , e := range manifest .Emotes {
45+ emotes [i ] = e .Name
46+ }
47+ return emotes , nil
48+ }
49+
50+ func NewEntityExtractor () (* EntityExtractor , error ) {
51+ emotes , err := loadEmoteManifest ()
52+ if err != nil {
53+ return nil , err
54+ }
55+
56+ return & EntityExtractor {
57+ parserCtx : parser .NewParserContext (parser.ParserContextValues {
58+ Emotes : emotes ,
59+ Nicks : []string {},
60+ Tags : []string {"nsfw" , "weeb" , "nsfl" , "loud" },
61+ EmoteModifiers : []string {"mirror" , "flip" , "rain" , "snow" , "rustle" , "worth" , "love" , "spin" , "wide" , "lag" , "hyper" },
62+ }),
63+ urls : xurls .Relaxed (),
64+ }, nil
65+ }
66+
67+ type EntityExtractor struct {
68+ parserCtx * parser.ParserContext
69+ urls * regexp.Regexp
70+ }
71+
72+ func (x * EntityExtractor ) scheduleEmoteSync () {
73+ for range time .NewTicker (time .Minute ).C {
74+ emotes , err := loadEmoteManifest ()
75+ if err != nil {
76+ log .Println ("failed to update emotes: %v" , err )
77+ continue
78+ }
79+ x .parserCtx .Emotes .Replace (parser .RunesFromStrings (emotes ))
80+ }
81+ }
82+
83+ func (x * EntityExtractor ) AddNick (emote string ) {
84+ x .parserCtx .Nicks .Insert ([]rune (emote ))
85+ }
86+
87+ func (x * EntityExtractor ) RemoveNick (emote string ) {
88+ x .parserCtx .Nicks .Remove ([]rune (emote ))
89+ }
90+
91+ func (x * EntityExtractor ) Extract (msg string ) * Entities {
1492 e := & Entities {}
15- addEntitiesFromSpan (e , parser .NewParser (parserCtx , parser .NewLexer (msg )).ParseMessage ())
93+ addEntitiesFromSpan (e , parser .NewParser (x . parserCtx , parser .NewLexer (msg )).ParseMessage ())
1694
17- for _ , b := range urls .FindAllStringIndex (msg , - 1 ) {
95+ for _ , b := range x . urls .FindAllStringIndex (msg , - 1 ) {
1896 e .Links = append (e .Links , & Link {
1997 URL : msg [b [0 ]:b [1 ]],
2098 Bounds : [2 ]int {b [0 ], b [1 ]},
@@ -35,17 +113,22 @@ func addEntitiesFromSpan(e *Entities, span *parser.Span) {
35113 Bounds : [2 ]int {span .Pos (), span .End ()},
36114 })
37115 case parser .SpanGreentext :
38- e .Greentexts = append ( e . Greentexts , & Greentext {
116+ e .Greentext = & Generic {
39117 Bounds : [2 ]int {span .Pos (), span .End ()},
40- })
118+ }
119+ case parser .SpanMe :
120+ e .Me = & Generic {
121+ Bounds : [2 ]int {span .Pos (), span .End ()},
122+ }
41123 }
42124
43125 for _ , ni := range span .Nodes {
44126 switch n := ni .(type ) {
45127 case * parser.Emote :
46128 e .Emotes = append (e .Emotes , & Emote {
47- Name : n .Name ,
48- Bounds : [2 ]int {n .Pos (), n .End ()},
129+ Name : n .Name ,
130+ Modifiers : n .Modifiers ,
131+ Bounds : [2 ]int {n .Pos (), n .End ()},
49132 })
50133 case * parser.Nick :
51134 e .Nicks = append (e .Nicks , & Nick {
@@ -69,8 +152,9 @@ type Link struct {
69152}
70153
71154type Emote struct {
72- Name string `json:"name,omitempty"`
73- Bounds [2 ]int `json:"bounds,omitempty"`
155+ Name string `json:"name,omitempty"`
156+ Modifiers []string `json:"modifiers,omitempty"`
157+ Bounds [2 ]int `json:"bounds,omitempty"`
74158}
75159
76160type Nick struct {
@@ -91,18 +175,19 @@ type Spoiler struct {
91175 Bounds [2 ]int `json:"bounds,omitempty"`
92176}
93177
94- type Greentext struct {
178+ type Generic struct {
95179 Bounds [2 ]int `json:"bounds,omitempty"`
96180}
97181
98182type Entities struct {
99- Links []* Link `json:"links,omitempty"`
100- Emotes []* Emote `json:"emotes,omitempty"`
101- Nicks []* Nick `json:"nicks,omitempty"`
102- Tags []* Tag `json:"tags,omitempty"`
103- Codes []* Code `json:"codes,omitempty"`
104- Spoilers []* Spoiler `json:"spoilers,omitempty"`
105- Greentexts []* Greentext `json:"greentexts,omitempty"`
183+ Links []* Link `json:"links,omitempty"`
184+ Emotes []* Emote `json:"emotes,omitempty"`
185+ Nicks []* Nick `json:"nicks,omitempty"`
186+ Tags []* Tag `json:"tags,omitempty"`
187+ Codes []* Code `json:"codes,omitempty"`
188+ Spoilers []* Spoiler `json:"spoilers,omitempty"`
189+ Greentext * Generic `json:"greentext,omitempty"`
190+ Me * Generic `json:"me,omitempty"`
106191}
107192
108193func getEmotes () ([]string , error ) {
0 commit comments