diff options
Diffstat (limited to 'api/chat/chat.go')
-rw-r--r-- | api/chat/chat.go | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/api/chat/chat.go b/api/chat/chat.go new file mode 100644 index 0000000..51ee47d --- /dev/null +++ b/api/chat/chat.go @@ -0,0 +1,195 @@ +package chat + +import ( + "encoding/json" + "github.com/golang-jwt/jwt/v5" + "io" + "log" + "net/http" + "strings" + "time" + + "git.simponic.xyz/simponic/phoneof/adapters/messaging" + "git.simponic.xyz/simponic/phoneof/api/types" + "git.simponic.xyz/simponic/phoneof/database" +) + +func ValidateFren(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { + return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { + fren_id := req.FormValue("fren_id") + fren, err := database.FindFren(context.DBConn, fren_id) + if err != nil || fren == nil { + log.Printf("err fetching friend %s %s", fren, err) + resp.WriteHeader(http.StatusUnauthorized) + return failure(context, req, resp) + } + context.User = fren + (*context.TemplateData)["User"] = fren + return success(context, req, resp) + } +} + +func FetchMessagesContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { + return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { + before := time.Now().UTC() + var err error + if req.FormValue("before") != "" { + before, err = time.Parse(time.RFC3339, req.FormValue("before")) + if err != nil { + log.Printf("bad time: %s", err) + resp.WriteHeader(http.StatusBadRequest) + return failure(context, req, resp) + } + } + + query := database.ListMessageQuery{ + FrenId: context.User.Id, + Before: before, + Limit: 25, + } + messages, err := database.ListMessages(context.DBConn, query) + if err != nil { + log.Printf("err listing messages %v %s", query, err) + resp.WriteHeader(http.StatusInternalServerError) + return failure(context, req, resp) + } + + (*context.TemplateData)["Messages"] = messages + return success(context, req, resp) + } +} + +func SendMessageContinuation(messagingAdapter messaging.MessagingAdapter) func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { + return func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { + return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { + rawMessage := req.FormValue("message") + now := time.Now().UTC() + messageRequestId, err := messagingAdapter.SendMessage(context.User.Name + " " + rawMessage) + if err != nil { + log.Printf("err sending message %s %s %s", context.User, rawMessage, err) + // yeah this might be a 400 or whatever, i’ll fix it later + resp.WriteHeader(http.StatusInternalServerError) + return failure(context, req, resp) + } + + message, err := database.SaveMessage(context.DBConn, &database.Message{ + Id: messageRequestId, + FrenId: context.User.Id, + Message: rawMessage, + Time: now, + FrenSent: true, + }) + + log.Printf("Saved message %v", message) + return success(context, req, resp) + } + } +} + +type Timestamp struct { + time.Time +} + +func (p *Timestamp) UnmarshalJSON(bytes []byte) error { + var raw string + err := json.Unmarshal(bytes, &raw) + + if err != nil { + log.Printf("error decoding timestamp: %s\n", err) + return err + } + + p.Time, err = time.Parse(time.RFC3339, raw) + if err != nil { + log.Printf("error decoding timestamp: %s\n", err) + return err + } + + return nil +} + +type HttpSmsEventData struct { + Contact string `json:"contact"` + Content string `json:"content"` + Owner string `json:"owner"` + Timestamp time.Time `json:"timestamp"` +} +type HttpSmsEvent struct { + Data HttpSmsEventData `json:"data"` + Type string `json:"type"` + Id string `json:"id"` +} + +func ChatEventProcessorContinuation(signingKey string) func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { + return func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { + return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { + // check signing + joken := strings.Split(req.Header.Get("Authorization"), "Bearer ") + _, err := jwt.Parse(joken[1], func(token *jwt.Token) (interface{}, error) { + return []byte(signingKey), nil + }) + if err != nil { + log.Printf("invalid jwt %s", err) + resp.WriteHeader(http.StatusBadRequest) + return failure(context, req, resp) + } + + // decode the event + defer req.Body.Close() + body, err := io.ReadAll(req.Body) + if err != nil { + log.Printf("err reading body") + resp.WriteHeader(http.StatusInternalServerError) + return failure(context, req, resp) + } + + var event HttpSmsEvent + err = json.Unmarshal(body, &event) + if err != nil { + log.Printf("err unmarshaling body") + resp.WriteHeader(http.StatusBadRequest) + return failure(context, req, resp) + } + + // we only care about received messages + if event.Type != "message.phone.received" { + log.Printf("got non-receive event %s", event.Type) + return success(context, req, resp) + } + + // respond to texts with "<fren name> <content>" + content := strings.SplitN(event.Data.Content, " ", 2) + if len(content) < 2 { + log.Printf("no space delimiter") + resp.WriteHeader(http.StatusBadRequest) + return failure(context, req, resp) + } + name := content[0] + rawMessage := content[1] + + fren, err := database.FindFrenByName(context.DBConn, name) + if err != nil { + log.Printf("err when getting fren %s %s", name, err) + resp.WriteHeader(http.StatusBadRequest) + return failure(context, req, resp) + } + + // save the message! + _, err = database.SaveMessage(context.DBConn, &database.Message{ + Id: event.Id, + FrenId: fren.Id, + Message: rawMessage, + Time: event.Data.Timestamp, + FrenSent: false, + }) + if err != nil { + log.Printf("err when saving message %s %s", name, err) + resp.WriteHeader(http.StatusInternalServerError) + return failure(context, req, resp) + } + + return success(context, req, resp) + } + } + +} |