@@ -21,6 +21,7 @@ import (
21
21
"github.com/prometheus/client_golang/prometheus/promhttp"
22
22
23
23
"github.com/zephyrtronium/robot/brain"
24
+ "github.com/zephyrtronium/robot/userhash"
24
25
)
25
26
26
27
func (robo * Robot ) api (ctx context.Context , listen string , mux * http.ServeMux , metrics []prometheus.Collector ) error {
@@ -44,6 +45,7 @@ func (robo *Robot) api(ctx context.Context, listen string, mux *http.ServeMux, m
44
45
mux .HandleFunc ("GET /debug/pprof/symbol" , pprof .Symbol )
45
46
mux .HandleFunc ("GET /debug/pprof/trace" , pprof .Trace )
46
47
mux .HandleFunc ("GET /api/message/{tag...}" , robo .apiRecall )
48
+ mux .HandleFunc ("POST /api/message/{tag...}" , robo .apiLearn )
47
49
mux .HandleFunc ("DELETE /api/message/{tag...}" , robo .apiForget )
48
50
l , err := net .Listen ("tcp" , listen )
49
51
if err != nil {
@@ -149,6 +151,58 @@ func (robo *Robot) apiRecall(w http.ResponseWriter, r *http.Request) {
149
151
}
150
152
}
151
153
154
+ func (robo * Robot ) apiLearn (w http.ResponseWriter , r * http.Request ) {
155
+ ctx := r .Context ()
156
+ log := slog .With (slog .String ("api" , "recall" ), slog .Any ("trace" , uuid .New ()))
157
+ log .InfoContext (ctx , "handle" , slog .String ("route" , r .Pattern ), slog .String ("remote" , r .RemoteAddr ))
158
+ defer log .InfoContext (ctx , "done" )
159
+ tag := r .PathValue ("tag" )
160
+ d := jsontext .NewDecoder (r .Body )
161
+ var all error
162
+ var msg apiMessage
163
+ for {
164
+ err := json .UnmarshalDecode (d , & msg )
165
+ switch err {
166
+ case nil : // do nothing
167
+ case io .EOF :
168
+ // Done; transmit any learn errors.
169
+ if all != nil {
170
+ jsonerror (w , http .StatusInternalServerError , all .Error ())
171
+ return
172
+ }
173
+ w .WriteHeader (http .StatusNoContent )
174
+ return
175
+ default :
176
+ log .ErrorContext (ctx , "read message" , slog .Any ("err" , err ))
177
+ jsonerror (w , http .StatusBadRequest , "message read failed" )
178
+ return
179
+ }
180
+ m := brain.Message {
181
+ ID : msg .ID ,
182
+ Sender : userhash.Hash {'A' , 'P' , 'I' },
183
+ Text : msg .Text ,
184
+ }
185
+ if m .ID == "" {
186
+ m .ID = "API:" + uuid .NewString ()
187
+ }
188
+ if msg .Time == "" {
189
+ m .Timestamp = time .Now ().UnixMilli ()
190
+ } else {
191
+ t , err := time .Parse (time .RFC3339 , msg .Time )
192
+ if err != nil {
193
+ all = errors .Join (all , err )
194
+ continue
195
+ }
196
+ m .Timestamp = t .UnixMilli ()
197
+ }
198
+ if err := brain .Learn (ctx , robo .brain , tag , & m ); err != nil {
199
+ log .ErrorContext (ctx , "learn failed" , slog .String ("tag" , tag ), slog .String ("id" , m .ID ), slog .Any ("err" , err ))
200
+ all = errors .Join (all , err )
201
+ // continue on
202
+ }
203
+ }
204
+ }
205
+
152
206
func (robo * Robot ) apiForget (w http.ResponseWriter , r * http.Request ) {
153
207
ctx := r .Context ()
154
208
log := slog .With (slog .String ("api" , "recall" ), slog .Any ("trace" , uuid .New ()))
0 commit comments