phoneof/api/chat/chat.go
Elizabeth Hunt f163a24279
All checks were successful
continuous-integration/drone/push Build is passing
initial commit
2025-01-03 01:47:07 -08:00

196 lines
5.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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, ill 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)
}
}
}