diff --git a/cmd/chat/main.go b/cmd/chat/main.go index bbe2fef8b..5650a607c 100644 --- a/cmd/chat/main.go +++ b/cmd/chat/main.go @@ -50,7 +50,7 @@ func main() { } if text[:1] == "/" { - cmdSplit := strings.Split(text, " ") + cmdSplit := strings.SplitN(text, " ", 2) switch cmdSplit[0] { case "/join": if len(cmdSplit) < 2 { @@ -58,7 +58,7 @@ func main() { } else { joinCh(conn, cmdSplit[1]) chanId = cmdSplit[1] - openCh(conn, chanId) + msgHistory(conn, chanId) } case "/part": partCh(conn, chanId) @@ -67,11 +67,15 @@ func main() { case "/list": listCh(conn) + case "/raw": + sendRaw(conn, cmdSplit[1]) + default: println("Unknown command, try:") println(" /join ") println(" /part") println(" /list") + println(" /raw ") } } else { sendMsg(conn, text, chanId) @@ -119,8 +123,12 @@ func partCh(conn *websocket.Conn, channelId string) { should(conn.WriteMessage(websocket.TextMessage, pb)) } -func openCh(conn *websocket.Conn, channelId string) { - pb, err := json.Marshal(incoming.Payload{ChannelOpen: &incoming.ChannelOpen{ChannelID: channelId}}) +func msgHistory(conn *websocket.Conn, channelId string) { + pb, err := json.Marshal(incoming.Payload{MessageHistory: &incoming.MessageHistory{ChannelID: channelId}}) should(err) should(conn.WriteMessage(websocket.TextMessage, pb)) } + +func sendRaw(conn *websocket.Conn, msg string) { + should(conn.WriteMessage(websocket.TextMessage, []byte(msg))) +} diff --git a/sam/service/message.go b/sam/service/message.go index d2d2858ff..719248fe8 100644 --- a/sam/service/message.go +++ b/sam/service/message.go @@ -51,7 +51,7 @@ func (svc message) Find(ctx context.Context, filter *types.MessageFilter) ([]*ty // @todo get user from context var currentUserID uint64 = 0 - // @todo verify if current user can access & write to this channel + // @todo verify if current user can access & read from this channel _ = currentUserID _ = filter.ChannelID diff --git a/sam/websocket/incoming.go b/sam/websocket/incoming.go index 3847bfbbb..300a8bd22 100644 --- a/sam/websocket/incoming.go +++ b/sam/websocket/incoming.go @@ -22,6 +22,8 @@ func (s *Session) dispatch(raw []byte) (err error) { return s.messageUpdate(ctx, p.MessageUpdate) case p.MessageDelete != nil: return s.messageDelete(ctx, p.MessageDelete) + case p.MessageHistory != nil: + return s.messageHistory(ctx, p.MessageHistory) // channel actions case p.ChannelJoin != nil: @@ -30,8 +32,10 @@ func (s *Session) dispatch(raw []byte) (err error) { return s.channelPart(ctx, p.ChannelPart) case p.ChannelList != nil: return s.channelList(ctx, p.ChannelList) - case p.ChannelOpen != nil: - return s.channelOpen(ctx, p.ChannelOpen) + case p.ChannelRename != nil: + return s.channelRename(ctx, p.ChannelRename) + case p.ChannelChangeTopic != nil: + return s.channelChangeTopic(ctx, p.ChannelChangeTopic) } diff --git a/sam/websocket/incoming/channel.go b/sam/websocket/incoming/channel.go index 3535ac6dd..a06511846 100644 --- a/sam/websocket/incoming/channel.go +++ b/sam/websocket/incoming/channel.go @@ -11,9 +11,13 @@ type ( ChannelID string `json:"cid"` } - ChannelOpen struct { + ChannelRename struct { ChannelID string `json:"cid"` - Since string `json:"since,omitempty"` - Until string `json:"until,omitempty"` + Name string `json:"name"` + } + + ChannelChangeTopic struct { + ChannelID string `json:"cid"` + Topic string `json:"topic"` } ) diff --git a/sam/websocket/incoming/messages.go b/sam/websocket/incoming/messages.go index 85a94ad82..2f66f3c7d 100644 --- a/sam/websocket/incoming/messages.go +++ b/sam/websocket/incoming/messages.go @@ -15,4 +15,10 @@ type ( ChannelID string `json:"cid"` ID string `json:"id"` } + + MessageHistory struct { + ChannelID string `json:"cid"` + FromID string `json:"fid,omitempty"` + UntilID string `json:"uid,omitempty"` + } ) diff --git a/sam/websocket/incoming/payload.go b/sam/websocket/incoming/payload.go index 0caf8674e..670ca64e5 100644 --- a/sam/websocket/incoming/payload.go +++ b/sam/websocket/incoming/payload.go @@ -6,8 +6,11 @@ type Payload struct { *ChannelJoin `json:"chjoin"` *ChannelPart `json:"chpart"` + *ChannelChangeTopic `json:"chct"` + *ChannelRename `json:"chrn"` + // Get channel message history - *ChannelOpen `json:"chopen"` + *MessageHistory `json:"chopen"` // Message actions *MessageCreate `json:"msgcre"` diff --git a/sam/websocket/incoming_channel.go b/sam/websocket/incoming_channel.go index 7f00915b1..963e12df2 100644 --- a/sam/websocket/incoming_channel.go +++ b/sam/websocket/incoming_channel.go @@ -4,7 +4,6 @@ import ( "context" "github.com/crusttech/crust/auth" "github.com/crusttech/crust/sam/service" - "github.com/crusttech/crust/sam/types" "github.com/crusttech/crust/sam/websocket/incoming" "github.com/crusttech/crust/sam/websocket/outgoing" ) @@ -52,18 +51,44 @@ func (s *Session) channelList(ctx context.Context, p *incoming.ChannelList) erro return s.sendReply(payloadFromChannels(channels)) } -func (s *Session) channelOpen(ctx context.Context, p *incoming.ChannelOpen) error { - var ( - filter = &types.MessageFilter{ - ChannelID: parseUInt64(p.ChannelID), - FromMessageID: parseUInt64(p.Since), - } - ) - - messages, err := service.Message().Find(ctx, filter) +func (s *Session) channelRename(ctx context.Context, p *incoming.ChannelRename) error { + ch, err := service.Channel().FindByID(ctx, parseUInt64(p.ChannelID)) if err != nil { return err } - return s.sendReply(payloadFromMessages(messages)) + if ch.Name == p.Name { + // No changes, ignore + return nil + } + + ch.Name = p.Name + + ch, err = service.Channel().Update(ctx, ch) + if err != nil { + return err + } + + return s.sendToAllSubscribers(payloadFromChannel(ch), p.ChannelID) +} + +func (s *Session) channelChangeTopic(ctx context.Context, p *incoming.ChannelChangeTopic) error { + ch, err := service.Channel().FindByID(ctx, parseUInt64(p.ChannelID)) + if err != nil { + return err + } + + if ch.Topic == p.Topic { + // No changes, ignore + return nil + } + + ch.Topic = p.Topic + + ch, err = service.Channel().Update(ctx, ch) + if err != nil { + return err + } + + return s.sendToAllSubscribers(payloadFromChannel(ch), p.ChannelID) } diff --git a/sam/websocket/incoming_message.go b/sam/websocket/incoming_message.go index 5c16f58fe..460dfb455 100644 --- a/sam/websocket/incoming_message.go +++ b/sam/websocket/incoming_message.go @@ -56,3 +56,23 @@ func (s *Session) messageDelete(ctx context.Context, p *incoming.MessageDelete) return s.sendToAllSubscribers(&outgoing.MessageDelete{ID: p.ID}, p.ChannelID) } + +func (s *Session) messageHistory(ctx context.Context, p *incoming.MessageHistory) error { + var ( + filter = &types.MessageFilter{ + ChannelID: parseUInt64(p.ChannelID), + FromMessageID: parseUInt64(p.FromID), + UntilMessageID: parseUInt64(p.UntilID), + + // Max no. of messages we will return + Limit: 50, + } + ) + + messages, err := service.Message().Find(ctx, filter) + if err != nil { + return err + } + + return s.sendReply(payloadFromMessages(messages)) +} diff --git a/sam/websocket/outgoing/channel.go b/sam/websocket/outgoing/channel.go index 6c994f01b..b3df122f1 100644 --- a/sam/websocket/outgoing/channel.go +++ b/sam/websocket/outgoing/channel.go @@ -23,8 +23,10 @@ type ( Channel struct { // Channel to part (nil) for ALL channels - ID string `json:"id"` - Name string `json:"name"` + ID string `json:"id"` + Name string `json:"name"` + Topic string `json:"topic"` + LastMessageID string `json:"lastMessageId"` } Channels []*Channel diff --git a/sam/websocket/payload.go b/sam/websocket/payload.go index dfd6edf2b..645613d0a 100644 --- a/sam/websocket/payload.go +++ b/sam/websocket/payload.go @@ -3,17 +3,16 @@ package websocket import ( "github.com/crusttech/crust/sam/types" "github.com/crusttech/crust/sam/websocket/outgoing" - "strconv" ) func payloadFromMessage(msg *types.Message) *outgoing.Message { return &outgoing.Message{ Message: msg.Message, - ID: strconv.FormatUint(msg.ID, 10), - ChannelID: strconv.FormatUint(msg.ChannelID, 10), + ID: uint64toa(msg.ID), + ChannelID: uint64toa(msg.ChannelID), Type: msg.Type, - UserID: strconv.FormatUint(msg.UserID, 10), - ReplyTo: strconv.FormatUint(msg.ReplyTo, 10), + UserID: uint64toa(msg.UserID), + ReplyTo: uint64toa(msg.ReplyTo), CreatedAt: msg.CreatedAt, UpdatedAt: msg.UpdatedAt, @@ -31,8 +30,10 @@ func payloadFromMessages(msg []*types.Message) *outgoing.Messages { func payloadFromChannel(ch *types.Channel) *outgoing.Channel { return &outgoing.Channel{ - ID: strconv.FormatUint(ch.ID, 10), - Name: ch.Name, + ID: uint64toa(ch.ID), + Name: ch.Name, + LastMessageID: uint64toa(ch.LastMessageID), + Topic: ch.Topic, } }