@@ -4,17 +4,95 @@ import (
4
4
"encoding/json"
5
5
"fmt"
6
6
"io/ioutil"
7
+ "log"
7
8
"net/http"
8
9
"regexp"
10
+ "time"
9
11
10
12
parser "github.com/MemeLabs/chat-parser"
13
+ "mvdan.cc/xurls/v2"
11
14
)
12
15
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 {
14
92
e := & Entities {}
15
- addEntitiesFromSpan (e , parser .NewParser (parserCtx , parser .NewLexer (msg )).ParseMessage ())
93
+ addEntitiesFromSpan (e , parser .NewParser (x . parserCtx , parser .NewLexer (msg )).ParseMessage ())
16
94
17
- for _ , b := range urls .FindAllStringIndex (msg , - 1 ) {
95
+ for _ , b := range x . urls .FindAllStringIndex (msg , - 1 ) {
18
96
e .Links = append (e .Links , & Link {
19
97
URL : msg [b [0 ]:b [1 ]],
20
98
Bounds : [2 ]int {b [0 ], b [1 ]},
@@ -35,17 +113,22 @@ func addEntitiesFromSpan(e *Entities, span *parser.Span) {
35
113
Bounds : [2 ]int {span .Pos (), span .End ()},
36
114
})
37
115
case parser .SpanGreentext :
38
- e .Greentexts = append ( e . Greentexts , & Greentext {
116
+ e .Greentext = & Generic {
39
117
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
+ }
41
123
}
42
124
43
125
for _ , ni := range span .Nodes {
44
126
switch n := ni .(type ) {
45
127
case * parser.Emote :
46
128
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 ()},
49
132
})
50
133
case * parser.Nick :
51
134
e .Nicks = append (e .Nicks , & Nick {
@@ -69,8 +152,9 @@ type Link struct {
69
152
}
70
153
71
154
type 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"`
74
158
}
75
159
76
160
type Nick struct {
@@ -91,18 +175,19 @@ type Spoiler struct {
91
175
Bounds [2 ]int `json:"bounds,omitempty"`
92
176
}
93
177
94
- type Greentext struct {
178
+ type Generic struct {
95
179
Bounds [2 ]int `json:"bounds,omitempty"`
96
180
}
97
181
98
182
type 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"`
106
191
}
107
192
108
193
func getEmotes () ([]string , error ) {
0 commit comments