mirror of
https://github.com/kevin-DL/services.git
synced 2026-01-19 22:15:24 +00:00
Add chat client
This commit is contained in:
121
chat/client/main.go
Normal file
121
chat/client/main.go
Normal file
@@ -0,0 +1,121 @@
|
||||
// Package main is a client for the chat service to demonstrate how it would work for a client. To
|
||||
// run the client, first launch the chat service by running `micro run ./chat` from the top level of
|
||||
// this repo. Then run `micro run ./chat/client` and `micro logs -f client` to follow the logs of
|
||||
// the client.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/micro/micro/v3/service"
|
||||
"github.com/micro/micro/v3/service/context/metadata"
|
||||
"github.com/micro/micro/v3/service/logger"
|
||||
chat "github.com/micro/services/chat/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
userOneID = "user-one-" + uuid.New().String()
|
||||
userTwoID = "user-two-" + uuid.New().String()
|
||||
)
|
||||
|
||||
func main() {
|
||||
// create a chat service client
|
||||
srv := service.New()
|
||||
cli := chat.NewChatService("chat", srv.Client())
|
||||
|
||||
// create a chat for our users
|
||||
userIDs := []string{userOneID, userTwoID}
|
||||
nRsp, err := cli.New(context.TODO(), &chat.NewRequest{UserIds: userIDs})
|
||||
if err != nil {
|
||||
logger.Fatalf("Error creating the chat: %v", err)
|
||||
}
|
||||
chatID := nRsp.GetChatId()
|
||||
logger.Infof("Chat Created. ID: %v", chatID)
|
||||
|
||||
// list the number messages in the chat history
|
||||
hRsp, err := cli.History(context.TODO(), &chat.HistoryRequest{ChatId: chatID})
|
||||
if err != nil {
|
||||
logger.Fatalf("Error getting the chat history: %v", err)
|
||||
}
|
||||
logger.Infof("Chat has %v message(s)", len(hRsp.Messages))
|
||||
|
||||
// create a channel to handle errors
|
||||
errChan := make(chan error)
|
||||
|
||||
// run user one
|
||||
go func() {
|
||||
ctx := metadata.NewContext(context.TODO(), metadata.Metadata{
|
||||
"user-id": userOneID, "chat-id": chatID,
|
||||
})
|
||||
stream, err := cli.Connect(ctx)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
for i := 1; true; i++ {
|
||||
// send a message to the chat
|
||||
err = stream.Send(&chat.Message{
|
||||
ClientId: uuid.New().String(),
|
||||
SentAt: time.Now().Unix(),
|
||||
Subject: "Message from user one",
|
||||
Text: fmt.Sprintf("Message #%v", i),
|
||||
})
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
logger.Infof("User one sent message")
|
||||
|
||||
// wait for user two to respond
|
||||
msg, err := stream.Recv()
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
logger.Infof("User one recieved message %v from %v", msg.Text, msg.UserId)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}()
|
||||
|
||||
// run user two
|
||||
go func() {
|
||||
ctx := metadata.NewContext(context.TODO(), metadata.Metadata{
|
||||
"user-id": userTwoID, "chat-id": chatID,
|
||||
})
|
||||
stream, err := cli.Connect(ctx)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
for i := 1; true; i++ {
|
||||
// send a response to the chat
|
||||
err = stream.Send(&chat.Message{
|
||||
ClientId: uuid.New().String(),
|
||||
SentAt: time.Now().Unix(),
|
||||
Subject: "Response from user two",
|
||||
Text: fmt.Sprintf("Response #%v", i),
|
||||
})
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
logger.Infof("User two sent message")
|
||||
|
||||
// wait for a message from user one
|
||||
msg, err := stream.Recv()
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
logger.Infof("User two recieved message %v from %v", msg.Text, msg.UserId)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}()
|
||||
|
||||
logger.Fatal(<-errChan)
|
||||
}
|
||||
@@ -7,12 +7,13 @@ import (
|
||||
|
||||
"github.com/micro/micro/v3/service/context/metadata"
|
||||
|
||||
// import the proto, it's standard to import the services own proto under the alias pb
|
||||
"github.com/google/uuid"
|
||||
"github.com/micro/micro/v3/service/errors"
|
||||
"github.com/micro/micro/v3/service/events"
|
||||
"github.com/micro/micro/v3/service/logger"
|
||||
"github.com/micro/micro/v3/service/store"
|
||||
|
||||
// it's standard to import the services own proto under the alias pb
|
||||
pb "github.com/micro/services/chat/proto"
|
||||
)
|
||||
|
||||
@@ -22,8 +23,9 @@ func New() pb.ChatHandler {
|
||||
}
|
||||
|
||||
const (
|
||||
storeKeyPrefix = "chat/"
|
||||
eventKeyPrefix = "chat/"
|
||||
chatStoreKeyPrefix = "chats/"
|
||||
chatEventKeyPrefix = "chats/"
|
||||
messageStoreKeyPrefix = "messages/"
|
||||
)
|
||||
|
||||
// handler satisfies the ChatHandler interface. You can see this inteface defined in chat.pb.micro.go
|
||||
@@ -58,11 +60,11 @@ func (h *handler) New(ctx context.Context, req *pb.NewRequest, rsp *pb.NewRespon
|
||||
sort.Strings(sortedIDs)
|
||||
|
||||
// key to lookup the chat in the store using, e.g. "chat/usera-userb-userc"
|
||||
key := storeKeyPrefix + strings.Join(sortedIDs, "-")
|
||||
key := chatStoreKeyPrefix + strings.Join(sortedIDs, "-")
|
||||
|
||||
// read from the store to check if a chat with these users already exists
|
||||
recs, err := store.Read(key)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
// if an error wasn't returned, at least one record was found. The value returned by the store
|
||||
// is the bytes representation of the chat id. We'll convert this back into a string and return
|
||||
// it to the client.
|
||||
@@ -80,9 +82,9 @@ func (h *handler) New(ctx context.Context, req *pb.NewRequest, rsp *pb.NewRespon
|
||||
// no chat id was returned so we'll generate one, write it to the store and then return it to the
|
||||
// client
|
||||
chatID := uuid.New().String()
|
||||
record := store.Record{Key: key, Value: []byte(chatID)}
|
||||
record := store.Record{Key: chatStoreKeyPrefix + chatID, Value: []byte(chatID)}
|
||||
if err := store.Write(&record); err != nil {
|
||||
logger.Errorf("Error writing to the store. Key: %v. Error: %v", key, err)
|
||||
logger.Errorf("Error writing to the store. Key: %v. Error: %v", record.Key, err)
|
||||
return errors.InternalServerError("chat.New.Unknown", "Error writing to the store")
|
||||
}
|
||||
|
||||
@@ -104,7 +106,7 @@ func (h *handler) History(ctx context.Context, req *pb.HistoryRequest, rsp *pb.H
|
||||
}
|
||||
|
||||
// lookup the chat from the store to ensure it's valid
|
||||
if _, err := store.Read(storeKeyPrefix + req.ChatId); err == store.ErrNotFound {
|
||||
if _, err := store.Read(chatStoreKeyPrefix + req.ChatId); err == store.ErrNotFound {
|
||||
return errors.BadRequest("chat.History.InvalidChatID", "Chat not found with this ID")
|
||||
} else if err != nil {
|
||||
logger.Errorf("Error reading from the store. Chat ID: %v. Error: %v", req.ChatId, err)
|
||||
@@ -114,7 +116,7 @@ func (h *handler) History(ctx context.Context, req *pb.HistoryRequest, rsp *pb.H
|
||||
// lookup the historical messages for the chat using the event store. lots of packages in micro
|
||||
// support options, in this case we'll pass the ReadLimit option to restrict the number of messages
|
||||
// we'll load from the events store.
|
||||
messages, err := events.Read(eventKeyPrefix+req.ChatId, events.ReadLimit(50))
|
||||
messages, err := events.Read(chatEventKeyPrefix+req.ChatId, events.ReadLimit(50))
|
||||
if err != nil {
|
||||
logger.Errorf("Error reading from the event store. Chat ID: %v. Error: %v", req.ChatId, err)
|
||||
return errors.InternalServerError("chat.History.Unknown", "Error reading from the event store")
|
||||
@@ -143,17 +145,17 @@ func (h *handler) History(ctx context.Context, req *pb.HistoryRequest, rsp *pb.H
|
||||
func (h *handler) Connect(ctx context.Context, stream pb.Chat_ConnectStream) error {
|
||||
// the client passed the chat id and user id in the request context. we'll load that information
|
||||
// now and validate it. If any information is missing we'll return a BadRequest error to the client
|
||||
userID, ok := metadata.Get(ctx, "UserID")
|
||||
userID, ok := metadata.Get(ctx, "user-id")
|
||||
if !ok {
|
||||
return errors.BadRequest("chat.Connect.MissingUserID", "UserID missing in context")
|
||||
}
|
||||
chatID, ok := metadata.Get(ctx, "ChatID")
|
||||
chatID, ok := metadata.Get(ctx, "chat-id")
|
||||
if !ok {
|
||||
return errors.BadRequest("chat.Connect.MissingChatID", "ChatId missing in context")
|
||||
}
|
||||
|
||||
// lookup the chat from the store to ensure it's valid
|
||||
if _, err := store.Read(storeKeyPrefix + chatID); err == store.ErrNotFound {
|
||||
if _, err := store.Read(chatStoreKeyPrefix + chatID); err == store.ErrNotFound {
|
||||
return errors.BadRequest("chat.Connect.InvalidChatID", "Chat not found with this ID")
|
||||
} else if err != nil {
|
||||
logger.Errorf("Error reading from the store. Chat ID: %v. Error: %v", chatID, err)
|
||||
@@ -175,9 +177,8 @@ func (h *handler) Connect(ctx context.Context, stream pb.Chat_ConnectStream) err
|
||||
|
||||
// create an event stream to consume messages posted by other users into the chat. we'll use the
|
||||
// user id as a queue to ensure each user recieves the message
|
||||
evStream, err := events.Subscribe(eventKeyPrefix+chatID, events.WithQueue(userID))
|
||||
evStream, err := events.Subscribe(chatEventKeyPrefix+chatID, events.WithQueue(userID))
|
||||
if err != nil {
|
||||
defer cancel()
|
||||
logger.Errorf("Error streaming events. Chat ID: %v. Error: %v", chatID, err)
|
||||
return errors.InternalServerError("chat.Connect.Unknown", "Error connecting to the event stream")
|
||||
}
|
||||
@@ -235,8 +236,26 @@ func (h *handler) Connect(ctx context.Context, stream pb.Chat_ConnectStream) err
|
||||
// an error occured in another goroutine, terminate the stream
|
||||
return err
|
||||
case msg := <-msgChan:
|
||||
// a message was recieved from the client, send it to the event stream
|
||||
if err := events.Publish(eventKeyPrefix+chatID, msg); err != nil {
|
||||
// a message was recieved from the client. validate it hasn't been recieved before
|
||||
if _, err := store.Read(messageStoreKeyPrefix + msg.ClientId); err == nil {
|
||||
// the message has already been processed
|
||||
continue
|
||||
} else if err != store.ErrNotFound {
|
||||
// an unexpected error occured
|
||||
return err
|
||||
}
|
||||
|
||||
// set the defaults
|
||||
msg.UserId = userID
|
||||
msg.ChatId = chatID
|
||||
|
||||
// send the message to the event stream
|
||||
if err := events.Publish(chatEventKeyPrefix+chatID, msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// record the messages client id
|
||||
if err := store.Write(&store.Record{Key: messageStoreKeyPrefix + msg.ClientId}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: proto/chat.proto
|
||||
// source: chat/proto/chat.proto
|
||||
|
||||
package chat
|
||||
|
||||
@@ -32,7 +32,7 @@ func (m *NewRequest) Reset() { *m = NewRequest{} }
|
||||
func (m *NewRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*NewRequest) ProtoMessage() {}
|
||||
func (*NewRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_ed7e7dde45555b7d, []int{0}
|
||||
return fileDescriptor_825b1469f80f958d, []int{0}
|
||||
}
|
||||
|
||||
func (m *NewRequest) XXX_Unmarshal(b []byte) error {
|
||||
@@ -72,7 +72,7 @@ func (m *NewResponse) Reset() { *m = NewResponse{} }
|
||||
func (m *NewResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*NewResponse) ProtoMessage() {}
|
||||
func (*NewResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_ed7e7dde45555b7d, []int{1}
|
||||
return fileDescriptor_825b1469f80f958d, []int{1}
|
||||
}
|
||||
|
||||
func (m *NewResponse) XXX_Unmarshal(b []byte) error {
|
||||
@@ -114,7 +114,7 @@ func (m *HistoryRequest) Reset() { *m = HistoryRequest{} }
|
||||
func (m *HistoryRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*HistoryRequest) ProtoMessage() {}
|
||||
func (*HistoryRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_ed7e7dde45555b7d, []int{2}
|
||||
return fileDescriptor_825b1469f80f958d, []int{2}
|
||||
}
|
||||
|
||||
func (m *HistoryRequest) XXX_Unmarshal(b []byte) error {
|
||||
@@ -154,7 +154,7 @@ func (m *HistoryResponse) Reset() { *m = HistoryResponse{} }
|
||||
func (m *HistoryResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*HistoryResponse) ProtoMessage() {}
|
||||
func (*HistoryResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_ed7e7dde45555b7d, []int{3}
|
||||
return fileDescriptor_825b1469f80f958d, []int{3}
|
||||
}
|
||||
|
||||
func (m *HistoryResponse) XXX_Unmarshal(b []byte) error {
|
||||
@@ -193,7 +193,7 @@ type Message struct {
|
||||
// id of the user who sent the message
|
||||
UserId string `protobuf:"bytes,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
|
||||
// time time the message was sent in unix format
|
||||
SentAt int32 `protobuf:"varint,5,opt,name=sent_at,json=sentAt,proto3" json:"sent_at,omitempty"`
|
||||
SentAt int64 `protobuf:"varint,5,opt,name=sent_at,json=sentAt,proto3" json:"sent_at,omitempty"`
|
||||
// subject of the message
|
||||
Subject string `protobuf:"bytes,6,opt,name=subject,proto3" json:"subject,omitempty"`
|
||||
// text of the message
|
||||
@@ -207,7 +207,7 @@ func (m *Message) Reset() { *m = Message{} }
|
||||
func (m *Message) String() string { return proto.CompactTextString(m) }
|
||||
func (*Message) ProtoMessage() {}
|
||||
func (*Message) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_ed7e7dde45555b7d, []int{4}
|
||||
return fileDescriptor_825b1469f80f958d, []int{4}
|
||||
}
|
||||
|
||||
func (m *Message) XXX_Unmarshal(b []byte) error {
|
||||
@@ -256,7 +256,7 @@ func (m *Message) GetUserId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Message) GetSentAt() int32 {
|
||||
func (m *Message) GetSentAt() int64 {
|
||||
if m != nil {
|
||||
return m.SentAt
|
||||
}
|
||||
@@ -285,30 +285,30 @@ func init() {
|
||||
proto.RegisterType((*Message)(nil), "chat.Message")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("proto/chat.proto", fileDescriptor_ed7e7dde45555b7d) }
|
||||
func init() { proto.RegisterFile("chat/proto/chat.proto", fileDescriptor_825b1469f80f958d) }
|
||||
|
||||
var fileDescriptor_ed7e7dde45555b7d = []byte{
|
||||
var fileDescriptor_825b1469f80f958d = []byte{
|
||||
// 351 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0x41, 0x4f, 0xf2, 0x40,
|
||||
0x10, 0xcd, 0x42, 0x69, 0x61, 0xc8, 0xc7, 0x87, 0x1b, 0x8d, 0x2b, 0x5e, 0x48, 0x0f, 0x5a, 0x24,
|
||||
0xa1, 0x06, 0x13, 0x2f, 0x7a, 0x51, 0x2e, 0x72, 0x90, 0x43, 0x8f, 0x5e, 0x48, 0x69, 0x37, 0xb0,
|
||||
0x46, 0xba, 0xd8, 0xd9, 0x8a, 0xfe, 0x12, 0x7f, 0x86, 0x7f, 0xd1, 0xec, 0x6e, 0x41, 0x30, 0xf1,
|
||||
0x36, 0xef, 0xbd, 0x99, 0xd9, 0x37, 0x2f, 0x0b, 0xed, 0x55, 0x2e, 0x95, 0x0c, 0x93, 0x45, 0xac,
|
||||
0x06, 0xa6, 0xa4, 0x8e, 0xae, 0xfd, 0x73, 0x80, 0x09, 0x5f, 0x47, 0xfc, 0xb5, 0xe0, 0xa8, 0xe8,
|
||||
0x09, 0xd4, 0x0b, 0xe4, 0xf9, 0x54, 0xa4, 0xc8, 0x48, 0xb7, 0x1a, 0x34, 0x22, 0x4f, 0xe3, 0x71,
|
||||
0x8a, 0xfe, 0x19, 0x34, 0x4d, 0x23, 0xae, 0x64, 0x86, 0x9c, 0x1e, 0x83, 0xa7, 0xe7, 0xa7, 0x22,
|
||||
0x65, 0xa4, 0x4b, 0x82, 0x46, 0xe4, 0x6a, 0x38, 0x4e, 0xfd, 0x1e, 0xb4, 0x1e, 0x04, 0x2a, 0x99,
|
||||
0x7f, 0x6c, 0x96, 0xfe, 0xd9, 0x7a, 0x0b, 0xff, 0xb7, 0xad, 0xe5, 0xda, 0x1e, 0xd4, 0x97, 0x1c,
|
||||
0x31, 0x9e, 0x73, 0x6b, 0xa0, 0x39, 0xfc, 0x37, 0x30, 0x9e, 0x1f, 0x2d, 0x1b, 0x6d, 0x65, 0xff,
|
||||
0x8b, 0x80, 0x57, 0xb2, 0xb4, 0x05, 0x95, 0xed, 0xf6, 0x8a, 0x48, 0xe9, 0x29, 0x34, 0x92, 0x17,
|
||||
0xc1, 0x33, 0xf3, 0x68, 0xc5, 0xd0, 0x75, 0x4b, 0x8c, 0xd3, 0x5d, 0x3f, 0xd5, 0x5d, 0x3f, 0x5a,
|
||||
0x28, 0xaf, 0x67, 0x8e, 0x15, 0xec, 0xf1, 0x5a, 0x40, 0xbd, 0x2c, 0x56, 0xac, 0xd6, 0x25, 0x41,
|
||||
0x2d, 0x72, 0x35, 0xbc, 0x53, 0x94, 0x81, 0x87, 0xc5, 0xec, 0x99, 0x27, 0x8a, 0xb9, 0x66, 0x62,
|
||||
0x03, 0x29, 0x05, 0x47, 0xf1, 0x77, 0xc5, 0x3c, 0x43, 0x9b, 0x7a, 0xf8, 0x49, 0xc0, 0x19, 0x2d,
|
||||
0x62, 0x45, 0x2f, 0xa0, 0x3a, 0xe1, 0x6b, 0xda, 0xb6, 0xa7, 0xfd, 0xe4, 0xdf, 0x39, 0xd8, 0x61,
|
||||
0xca, 0x44, 0xae, 0xc1, 0x2b, 0x43, 0xa2, 0x87, 0x56, 0xdd, 0x8f, 0xb7, 0x73, 0xf4, 0x8b, 0x2d,
|
||||
0xe7, 0xfa, 0xe0, 0x8d, 0x64, 0x96, 0x69, 0x2f, 0xfb, 0x11, 0x76, 0xf6, 0x61, 0x40, 0x2e, 0xc9,
|
||||
0x7d, 0xff, 0xa9, 0x37, 0x17, 0x6a, 0x51, 0xcc, 0x06, 0x89, 0x5c, 0x86, 0x4b, 0x91, 0xe4, 0x32,
|
||||
0x44, 0x9e, 0xbf, 0x89, 0x84, 0xa3, 0xf9, 0x33, 0xa1, 0xf9, 0x33, 0x37, 0xba, 0x9c, 0xb9, 0xa6,
|
||||
0xbe, 0xfa, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x71, 0x1b, 0x44, 0xc0, 0x53, 0x02, 0x00, 0x00,
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0xcd, 0x4e, 0xf2, 0x40,
|
||||
0x14, 0xcd, 0xd0, 0x7e, 0x1d, 0xb8, 0xe4, 0x43, 0x9d, 0x48, 0x1c, 0x71, 0x43, 0xba, 0xd0, 0x22,
|
||||
0x09, 0x35, 0x98, 0xb8, 0xd1, 0x8d, 0xb2, 0x91, 0x85, 0x2c, 0xba, 0x74, 0x43, 0x4a, 0x3b, 0x81,
|
||||
0x31, 0xd2, 0xc1, 0xce, 0x54, 0xf4, 0x49, 0x7c, 0x0c, 0x5f, 0xd1, 0xcc, 0x4c, 0xf9, 0xa9, 0x89,
|
||||
0xbb, 0xf3, 0x33, 0xf7, 0xf6, 0xdc, 0x93, 0x42, 0x3b, 0x59, 0xc4, 0x2a, 0x5c, 0xe5, 0x42, 0x89,
|
||||
0x50, 0xc3, 0x81, 0x81, 0xc4, 0xd5, 0xd8, 0xbf, 0x00, 0x98, 0xb0, 0x75, 0xc4, 0xde, 0x0a, 0x26,
|
||||
0x15, 0x39, 0x85, 0x7a, 0x21, 0x59, 0x3e, 0xe5, 0xa9, 0xa4, 0xa8, 0xeb, 0x04, 0x8d, 0x08, 0x6b,
|
||||
0x3e, 0x4e, 0xa5, 0x7f, 0x0e, 0x4d, 0xf3, 0x50, 0xae, 0x44, 0x26, 0x19, 0x39, 0x01, 0xac, 0xe7,
|
||||
0xa7, 0x3c, 0xa5, 0xa8, 0x8b, 0x82, 0x46, 0xe4, 0x69, 0x3a, 0x4e, 0xfd, 0x1e, 0xb4, 0x1e, 0xb9,
|
||||
0x54, 0x22, 0xff, 0xdc, 0x2c, 0xfd, 0xf3, 0xe9, 0x1d, 0x1c, 0x6c, 0x9f, 0x96, 0x6b, 0x7b, 0x50,
|
||||
0x5f, 0x32, 0x29, 0xe3, 0x39, 0xb3, 0x01, 0x9a, 0xc3, 0xff, 0x03, 0x93, 0xf9, 0xc9, 0xaa, 0xd1,
|
||||
0xd6, 0xf6, 0xbf, 0x11, 0xe0, 0x52, 0x25, 0x2d, 0xa8, 0x6d, 0xb7, 0xd7, 0x78, 0x4a, 0xce, 0xa0,
|
||||
0x91, 0xbc, 0x72, 0x96, 0x99, 0x8f, 0xd6, 0x8c, 0x5c, 0xb7, 0xc2, 0x38, 0xdd, 0xcf, 0xe3, 0xec,
|
||||
0xe7, 0xd1, 0x46, 0x79, 0x3d, 0x75, 0xad, 0x61, 0x8f, 0xd7, 0x86, 0xd4, 0xcb, 0x62, 0x45, 0xff,
|
||||
0x75, 0x51, 0xe0, 0x44, 0x9e, 0xa6, 0xf7, 0x8a, 0x50, 0xc0, 0xb2, 0x98, 0xbd, 0xb0, 0x44, 0x51,
|
||||
0xcf, 0x4c, 0x6c, 0x28, 0x21, 0xe0, 0x2a, 0xf6, 0xa1, 0x28, 0x36, 0xb2, 0xc1, 0xc3, 0x2f, 0x04,
|
||||
0xee, 0x68, 0x11, 0x2b, 0x72, 0x09, 0xce, 0x84, 0xad, 0xc9, 0xa1, 0x3d, 0x6d, 0xd7, 0x7f, 0xe7,
|
||||
0x68, 0x4f, 0x29, 0x1b, 0xb9, 0x01, 0x5c, 0x96, 0x44, 0x8e, 0xad, 0x5b, 0xad, 0xb7, 0xd3, 0xfe,
|
||||
0xa5, 0x96, 0x73, 0x7d, 0xc0, 0x23, 0x91, 0x65, 0x3a, 0x4b, 0xb5, 0xc2, 0x4e, 0x95, 0x06, 0xe8,
|
||||
0x0a, 0x3d, 0xf4, 0x9f, 0x7b, 0x73, 0xae, 0x16, 0xc5, 0x6c, 0x90, 0x88, 0x65, 0xb8, 0xe4, 0x49,
|
||||
0x2e, 0x42, 0xc9, 0xf2, 0x77, 0x9e, 0x30, 0x19, 0xee, 0x7e, 0x9f, 0x5b, 0x0d, 0x67, 0x9e, 0xc1,
|
||||
0xd7, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x53, 0xab, 0xf2, 0x77, 0x58, 0x02, 0x00, 0x00,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: proto/chat.proto
|
||||
// source: chat/proto/chat.proto
|
||||
|
||||
package chat
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ message Message {
|
||||
// id of the user who sent the message
|
||||
string user_id = 4;
|
||||
// time time the message was sent in unix format
|
||||
int32 sent_at = 5;
|
||||
int64 sent_at = 5;
|
||||
// subject of the message
|
||||
string subject = 6;
|
||||
// text of the message
|
||||
|
||||
Reference in New Issue
Block a user