diff --git a/channels/emoji.go b/channels/emoji.go
new file mode 100644
index 0000000000000000000000000000000000000000..f97d50889ccb3f931ee33b22f970b5b8747b9465
--- /dev/null
+++ b/channels/emoji.go
@@ -0,0 +1,6 @@
+package channels
+
+// ValidateReaction checks that the reaction only contains a single Emoji
+func ValidateReaction(reaction string) error {
+
+}
diff --git a/channels/eventModel.go b/channels/eventModel.go
index f86ec23e14ff8ca96574be95ddb65d3a5f03bc4c..08eb972610026dfd8770aabe55fa5f0f01e078ca 100644
--- a/channels/eventModel.go
+++ b/channels/eventModel.go
@@ -2,6 +2,7 @@ package channels
 
 import (
 	"errors"
+	"github.com/golang/protobuf/proto"
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/cmix/identity/receptionID"
 	"gitlab.com/elixxir/primitives/states"
@@ -26,22 +27,23 @@ type EventModel interface {
 	LeaveChannel(ChannelID *id.ID)
 
 	ReceiveMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID,
-		SenderUsername string, Content []byte,
+		SenderUsername string, text string,
 		timestamp time.Time, lease time.Duration, round rounds.Round)
 
 	ReceiveReply(ChannelID *id.ID, MessageID cryptoChannel.MessageID,
 		ReplyTo cryptoChannel.MessageID, SenderUsername string,
-		Content []byte, timestamp time.Time, lease time.Duration,
+		text string, timestamp time.Time, lease time.Duration,
 		round rounds.Round)
 	ReceiveReaction(ChannelID *id.ID, MessageID cryptoChannel.MessageID,
 		ReactionTo cryptoChannel.MessageID, SenderUsername string,
 		Reaction []byte, timestamp time.Time, lease time.Duration,
 		round rounds.Round)
 
-	IgnoreMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID)
-	UnIgnoreMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID)
-	PinMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID, end time.Time)
-	UnPinMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID)
+	//unimplemented
+	//IgnoreMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID)
+	//UnIgnoreMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID)
+	//PinMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID, end time.Time)
+	//UnPinMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID)
 }
 
 type MessageTypeReceiveMessage func(ChannelID *id.ID,
@@ -63,8 +65,9 @@ func initEvents(model EventModel) *events {
 	}
 
 	//set up default message types
-	e.registered[Text] = e.model.ReceiveTextMessage
-	e.registered[AdminText] = e.model.ReceiveAdminTextMessage
+	e.registered[Text] = e.receiveTextMessage
+	e.registered[AdminText] = e.receiveTextMessage
+	e.registered[Reaction] =
 	return e
 }
 
@@ -127,3 +130,70 @@ func (e *events) triggerAdminEvent(chID *id.ID, cm *ChannelMessage,
 		cm.Payload, round.Timestamps[states.QUEUED], time.Duration(cm.Lease), round)
 	return
 }
+
+func (e *events) receiveTextMessage(ChannelID *id.ID,
+	MessageID cryptoChannel.MessageID, messageType MessageType,
+	SenderUsername string, Content []byte, timestamp time.Time,
+	lease time.Duration, round rounds.Round) {
+	txt := &CMIXChannelText{}
+	if err := proto.Unmarshal(Content, txt); err != nil {
+		jww.ERROR.Printf("Failed to text unmarshal message %s from %s on "+
+			"channel %s, type %s, ts: %s, lease: %s, round: %d: %+v",
+			MessageID, SenderUsername, ChannelID, messageType, timestamp, lease,
+			round.ID, err)
+		return
+	}
+
+	if txt.ReplyMessageID != nil {
+		if len(txt.ReplyMessageID) == cryptoChannel.MessageIDLen {
+			var replyTo cryptoChannel.MessageID
+			copy(replyTo[:], txt.ReplyMessageID)
+			e.model.ReceiveReply(ChannelID, MessageID, replyTo, SenderUsername, txt.Text,
+				timestamp, lease, round)
+			return
+
+		} else {
+			jww.ERROR.Printf("Failed process reply to for message %s from %s on "+
+				"channel %s, type %s, ts: %s, lease: %s, round: %d, returning "+
+				"without reply",
+				MessageID, SenderUsername, ChannelID, messageType, timestamp, lease,
+				round.ID)
+		}
+	}
+
+	e.model.ReceiveMessage(ChannelID, MessageID, SenderUsername, txt.Text,
+		timestamp, lease, round)
+}
+
+func (e *events) receiveReaction(ChannelID *id.ID,
+	MessageID cryptoChannel.MessageID, messageType MessageType,
+	SenderUsername string, Content []byte, timestamp time.Time,
+	lease time.Duration, round rounds.Round) {
+	react := &CMIXChannelReaction{}
+	if err := proto.Unmarshal(Content, react); err != nil {
+		jww.ERROR.Printf("Failed to text unmarshal message %s from %s on "+
+			"channel %s, type %s, ts: %s, lease: %s, round: %d: %+v",
+			MessageID, SenderUsername, ChannelID, messageType, timestamp, lease,
+			round.ID, err)
+		return
+	}
+
+	if react.ReactionMessageID != nil && len(react.ReactionMessageID) == cryptoChannel.MessageIDLen {
+		var reactTo cryptoChannel.MessageID
+		copy(replyTo[:], react.ReactionMessageID)
+		e.model.ReceiveReply(ChannelID, MessageID, replyTo, SenderUsername, txt.Text,
+			timestamp, lease, round)
+		return
+
+	} else {
+		jww.ERROR.Printf("Failed process reply to for message %s from %s on "+
+			"channel %s, type %s, ts: %s, lease: %s, round: %d, returning "+
+			"without reply",
+			MessageID, SenderUsername, ChannelID, messageType, timestamp, lease,
+			round.ID)
+	}
+}
+
+e.model.ReceiveMessage(ChannelID, MessageID, SenderUsername, txt.Text,
+timestamp, lease, round)
+}
\ No newline at end of file
diff --git a/channels/interface.go b/channels/interface.go
index 8ecc115e96c73d7e8087e692a677fcc9fdcfd56d..f3a62ca7a49cb8c37b43393d2acf8824ab021a1a 100644
--- a/channels/interface.go
+++ b/channels/interface.go
@@ -27,7 +27,7 @@ type Manager interface {
 	SendReply(channelID *id.ID, msg string, replyTo cryptoChannel.MessageID,
 		validUntil time.Duration, params cmix.CMIXParams) (
 		cryptoChannel.MessageID, id.Round, ephemeral.Id, error)
-	SendReaction(channelID *id.ID, msg []byte,
+	SendReaction(channelID *id.ID, reaction string, reactTo cryptoChannel.MessageID,
 		validUntil time.Duration, params cmix.CMIXParams) (
 		cryptoChannel.MessageID, id.Round, ephemeral.Id, error)
 }
diff --git a/channels/joinedChannel.go b/channels/joinedChannel.go
index 0936b40eac894e6806d87ddc0faa77c230eafc42..81c34bb4884fab649b68c69f95e833c648947797 100644
--- a/channels/joinedChannel.go
+++ b/channels/joinedChannel.go
@@ -123,6 +123,22 @@ func (m *manager) addChannel(channel cryptoBroadcast.Channel) error {
 	return nil
 }
 
+func (m *manager) removeChannel(channelId *id.ID) error {
+	m.mux.Lock()
+	defer m.mux.Unlock()
+
+	ch, exists := m.channels[channelId]
+	if !exists {
+		return ChannelDoesNotExistsErr
+	}
+
+	ch.broadcast.Stop()
+
+	delete(m.channels, channelId)
+
+	return nil
+}
+
 //getChannel returns the given channel, if it exists
 func (m *manager) getChannel(channelId *id.ID) (*joinedChannel, error) {
 	m.mux.RLock()
diff --git a/channels/manager.go b/channels/manager.go
index 43ffa87b6de5ca4d574edac9064b906fce828bc2..bf8f820b671f9dfa4d4fc83bb753eff56a599b28 100644
--- a/channels/manager.go
+++ b/channels/manager.go
@@ -1,22 +1,14 @@
 package channels
 
 import (
-	"github.com/golang/protobuf/proto"
 	"gitlab.com/elixxir/client/broadcast"
-	"gitlab.com/elixxir/client/cmix"
 	"gitlab.com/elixxir/client/storage/versioned"
 	cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
 	"gitlab.com/elixxir/crypto/fastRNG"
-	"gitlab.com/xx_network/crypto/signature/rsa"
 	"gitlab.com/xx_network/primitives/id"
-	"gitlab.com/xx_network/primitives/id/ephemeral"
 	"sync"
-	"time"
 )
 
-const cmixChannelTextVerion = 0
-
 type manager struct {
 	//List of all channels
 	channels map[*id.ID]*joinedChannel
@@ -38,187 +30,19 @@ func NewManager() {
 }
 
 func (m *manager) JoinChannel(channel cryptoBroadcast.Channel) error {
-	return m.addChannel(channel)
-}
-
-func (m *manager) SendGeneric(channelID *id.ID, msg []byte, validUntil time.Duration,
-	messageType MessageType, params cmix.CMIXParams) (cryptoChannel.MessageID,
-	id.Round, ephemeral.Id, error) {
-
-	//find the channel
-	ch, err := m.getChannel(channelID)
+	err := m.addChannel(channel)
 	if err != nil {
-		return cryptoChannel.MessageID{}, 0, ephemeral.Id{}, err
+		return err
 	}
-
-	var msgId cryptoChannel.MessageID
-	//Note: we are not checking check if message is too long before trying to
-	//find a round
-
-	//Build the function pointer that will build the message
-	assemble := func(rid id.Round) ([]byte, error) {
-
-		//Build the message
-		chMsg := &ChannelMessage{
-			Lease:       validUntil.Nanoseconds(),
-			RoundID:     uint64(rid),
-			PayloadType: uint32(messageType),
-			Payload:     msg,
-		}
-
-		//Serialize the message
-		chMsgSerial, err := proto.Marshal(chMsg)
-		if err != nil {
-			return nil, err
-		}
-
-		//Sign the message
-		messageSig, err := m.name.SignChannelMessage(chMsgSerial)
-		if err != nil {
-			return nil, err
-		}
-
-		//Build the user message
-		validationSig, unameLease := m.name.GetChannelValidationSignature()
-
-		usrMsg := &UserMessage{
-			Message:             chMsgSerial,
-			ValidationSignature: validationSig,
-			Signature:           messageSig,
-			Username:            m.name.GetUsername(),
-			ECCPublicKey:        m.name.GetChannelPubkey(),
-			UsernameLease:       unameLease.Unix(),
-		}
-
-		//Serialize the user message
-		usrMsgSerial, err := proto.Marshal(usrMsg)
-		if err != nil {
-			return nil, err
-		}
-
-		//Fill in any extra bits in the payload to ensure it is the right size
-		usrMsgSerialSized, err := broadcast.NewSizedBroadcast(
-			ch.broadcast.MaxAsymmetricPayloadSize(), usrMsgSerial)
-		if err != nil {
-			return nil, err
-		}
-
-		msgId = cryptoChannel.MakeMessageID(usrMsgSerialSized)
-
-		return usrMsgSerialSized, nil
-	}
-
-	//TODO: send the send message over to reception manually so it is added to
-	//the database early
-	rid, ephid, err := ch.broadcast.BroadcastWithAssembler(assemble, params)
-	return msgId, rid, ephid, err
+	go m.events.model.JoinChannel(channel)
+	return nil
 }
 
-func (m *manager) SendAdminGeneric(privKey *rsa.PrivateKey, channelID *id.ID,
-	msg []byte, validUntil time.Duration, messageType MessageType,
-	params cmix.CMIXParams) (cryptoChannel.MessageID, id.Round, ephemeral.Id,
-	error) {
-
-	//find the channel
-	ch, err := m.getChannel(channelID)
+func (m *manager) LeaveChannel(channelId *id.ID) error {
+	err := m.removeChannel(channelId)
 	if err != nil {
-		return cryptoChannel.MessageID{}, 0, ephemeral.Id{}, err
+		return err
 	}
-
-	var msgId cryptoChannel.MessageID
-	//Note: we are not checking check if message is too long before trying to
-	//find a round
-
-	//Build the function pointer that will build the message
-	assemble := func(rid id.Round) ([]byte, error) {
-
-		//Build the message
-		chMsg := &ChannelMessage{
-			Lease:       validUntil.Nanoseconds(),
-			RoundID:     uint64(rid),
-			PayloadType: uint32(messageType),
-			Payload:     msg,
-		}
-
-		//Serialize the message
-		chMsgSerial, err := proto.Marshal(chMsg)
-		if err != nil {
-			return nil, err
-		}
-
-		//check if the message is too long
-		if len(chMsgSerial) > broadcast.MaxSizedBroadcastPayloadSize(privKey.Size()) {
-			return nil, MessageTooLongErr
-		}
-
-		//Fill in any extra bits in the payload to ensure it is the right size
-		chMsgSerialSized, err := broadcast.NewSizedBroadcast(
-			ch.broadcast.MaxAsymmetricPayloadSize(), chMsgSerial)
-		if err != nil {
-			return nil, err
-		}
-
-		msgId = cryptoChannel.MakeMessageID(chMsgSerialSized)
-
-		return chMsgSerialSized, nil
-	}
-
-	//TODO: send the send message over to reception manually so it is added to
-	//the database early
-	rid, ephid, err := ch.broadcast.BroadcastAsymmetricWithAssembler(privKey,
-		assemble, params)
-	return msgId, rid, ephid, err
-}
-
-func (m *manager) SendMessage(channelID *id.ID, msg string,
-	validUntil time.Duration, params cmix.CMIXParams) (
-	cryptoChannel.MessageID, id.Round, ephemeral.Id, error) {
-	txt := &CMIXChannelText{
-		Version:        cmixChannelTextVerion,
-		Text:           msg,
-		ReplyMessageID: nil,
-	}
-
-	txtMarshaled, err := proto.Marshal(txt)
-	if err != nil {
-		return cryptoChannel.MessageID{}, 0, ephemeral.Id{}, err
-	}
-
-	return m.SendGeneric(channelID, txtMarshaled, validUntil, Text, params)
-}
-
-func (m *manager) SendReply(channelID *id.ID, msg string,
-	replyTo cryptoChannel.MessageID, validUntil time.Duration,
-	params cmix.CMIXParams) (cryptoChannel.MessageID, id.Round, ephemeral.Id,
-	error) {
-	txt := &CMIXChannelText{
-		Version:        cmixChannelTextVerion,
-		Text:           msg,
-		ReplyMessageID: replyTo[:],
-	}
-
-	txtMarshaled, err := proto.Marshal(txt)
-	if err != nil {
-		return cryptoChannel.MessageID{}, 0, ephemeral.Id{}, err
-	}
-
-	return m.SendGeneric(channelID, txtMarshaled, validUntil, Text, params)
-}
-
-func (m *manager) SendReaction(channelID *id.ID, msg string,
-	replyTo cryptoChannel.MessageID, validUntil time.Duration,
-	params cmix.CMIXParams) (cryptoChannel.MessageID, id.Round, ephemeral.Id,
-	error) {
-	txt := &CMIXChannelText{
-		Version:        cmixChannelTextVerion,
-		Text:           msg,
-		ReplyMessageID: replyTo[:],
-	}
-
-	txtMarshaled, err := proto.Marshal(txt)
-	if err != nil {
-		return cryptoChannel.MessageID{}, 0, ephemeral.Id{}, err
-	}
-
-	return m.SendGeneric(channelID, txtMarshaled, validUntil, Text, params)
+	go m.events.model.LeaveChannel(channelId)
+	return nil
 }
diff --git a/channels/send.go b/channels/send.go
index d72c017e84aa8688cf59f7a04353b0f96da67b06..34dfeef454c1fb1484741553ddf30ae38915d76d 100644
--- a/channels/send.go
+++ b/channels/send.go
@@ -1 +1,205 @@
 package channels
+
+import (
+	"github.com/forPelevin/gomoji"
+	"gitlab.com/elixxir/client/broadcast"
+	"gitlab.com/elixxir/client/cmix"
+	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	"gitlab.com/xx_network/crypto/signature/rsa"
+	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/xx_network/primitives/id/ephemeral"
+	"google.golang.org/protobuf/proto"
+	"time"
+)
+
+const (
+	cmixChannelTextVersion     = 0
+	cmixChannelReactionVersion = 0
+)
+
+func (m *manager) SendGeneric(channelID *id.ID, msg []byte, validUntil time.Duration,
+	messageType MessageType, params cmix.CMIXParams) (cryptoChannel.MessageID,
+	id.Round, ephemeral.Id, error) {
+
+	//find the channel
+	ch, err := m.getChannel(channelID)
+	if err != nil {
+		return cryptoChannel.MessageID{}, 0, ephemeral.Id{}, err
+	}
+
+	var msgId cryptoChannel.MessageID
+	//Note: we are not checking check if message is too long before trying to
+	//find a round
+
+	//Build the function pointer that will build the message
+	assemble := func(rid id.Round) ([]byte, error) {
+
+		//Build the message
+		chMsg := &ChannelMessage{
+			Lease:       validUntil.Nanoseconds(),
+			RoundID:     uint64(rid),
+			PayloadType: uint32(messageType),
+			Payload:     msg,
+		}
+
+		//Serialize the message
+		chMsgSerial, err := proto.Marshal(chMsg)
+		if err != nil {
+			return nil, err
+		}
+
+		//Sign the message
+		messageSig, err := m.name.SignChannelMessage(chMsgSerial)
+		if err != nil {
+			return nil, err
+		}
+
+		//Build the user message
+		validationSig, unameLease := m.name.GetChannelValidationSignature()
+
+		usrMsg := &UserMessage{
+			Message:             chMsgSerial,
+			ValidationSignature: validationSig,
+			Signature:           messageSig,
+			Username:            m.name.GetUsername(),
+			ECCPublicKey:        m.name.GetChannelPubkey(),
+			UsernameLease:       unameLease.Unix(),
+		}
+
+		//Serialize the user message
+		usrMsgSerial, err := proto.Marshal(usrMsg)
+		if err != nil {
+			return nil, err
+		}
+
+		//Fill in any extra bits in the payload to ensure it is the right size
+		usrMsgSerialSized, err := broadcast.NewSizedBroadcast(
+			ch.broadcast.MaxAsymmetricPayloadSize(), usrMsgSerial)
+		if err != nil {
+			return nil, err
+		}
+
+		msgId = cryptoChannel.MakeMessageID(usrMsgSerialSized)
+
+		return usrMsgSerialSized, nil
+	}
+
+	//TODO: send the send message over to reception manually so it is added to
+	//the database early
+	rid, ephid, err := ch.broadcast.BroadcastWithAssembler(assemble, params)
+	return msgId, rid, ephid, err
+}
+
+func (m *manager) SendAdminGeneric(privKey *rsa.PrivateKey, channelID *id.ID,
+	msg []byte, validUntil time.Duration, messageType MessageType,
+	params cmix.CMIXParams) (cryptoChannel.MessageID, id.Round, ephemeral.Id,
+	error) {
+
+	//find the channel
+	ch, err := m.getChannel(channelID)
+	if err != nil {
+		return cryptoChannel.MessageID{}, 0, ephemeral.Id{}, err
+	}
+
+	var msgId cryptoChannel.MessageID
+	//Note: we are not checking check if message is too long before trying to
+	//find a round
+
+	//Build the function pointer that will build the message
+	assemble := func(rid id.Round) ([]byte, error) {
+
+		//Build the message
+		chMsg := &ChannelMessage{
+			Lease:       validUntil.Nanoseconds(),
+			RoundID:     uint64(rid),
+			PayloadType: uint32(messageType),
+			Payload:     msg,
+		}
+
+		//Serialize the message
+		chMsgSerial, err := proto.Marshal(chMsg)
+		if err != nil {
+			return nil, err
+		}
+
+		//check if the message is too long
+		if len(chMsgSerial) > broadcast.MaxSizedBroadcastPayloadSize(privKey.Size()) {
+			return nil, MessageTooLongErr
+		}
+
+		//Fill in any extra bits in the payload to ensure it is the right size
+		chMsgSerialSized, err := broadcast.NewSizedBroadcast(
+			ch.broadcast.MaxAsymmetricPayloadSize(), chMsgSerial)
+		if err != nil {
+			return nil, err
+		}
+
+		msgId = cryptoChannel.MakeMessageID(chMsgSerialSized)
+
+		return chMsgSerialSized, nil
+	}
+
+	//TODO: send the send message over to reception manually so it is added to
+	//the database early
+	rid, ephid, err := ch.broadcast.BroadcastAsymmetricWithAssembler(privKey,
+		assemble, params)
+	return msgId, rid, ephid, err
+}
+
+func (m *manager) SendMessage(channelID *id.ID, msg string,
+	validUntil time.Duration, params cmix.CMIXParams) (
+	cryptoChannel.MessageID, id.Round, ephemeral.Id, error) {
+	txt := &CMIXChannelText{
+		Version:        cmixChannelTextVersion,
+		Text:           msg,
+		ReplyMessageID: nil,
+	}
+
+	txtMarshaled, err := proto.Marshal(txt)
+	if err != nil {
+		return cryptoChannel.MessageID{}, 0, ephemeral.Id{}, err
+	}
+
+	return m.SendGeneric(channelID, txtMarshaled, validUntil, Text, params)
+}
+
+func (m *manager) SendReply(channelID *id.ID, msg string,
+	replyTo cryptoChannel.MessageID, validUntil time.Duration,
+	params cmix.CMIXParams) (cryptoChannel.MessageID, id.Round, ephemeral.Id,
+	error) {
+	txt := &CMIXChannelText{
+		Version:        cmixChannelTextVersion,
+		Text:           msg,
+		ReplyMessageID: replyTo[:],
+	}
+
+	txtMarshaled, err := proto.Marshal(txt)
+	if err != nil {
+		return cryptoChannel.MessageID{}, 0, ephemeral.Id{}, err
+	}
+
+	return m.SendGeneric(channelID, txtMarshaled, validUntil, Text, params)
+}
+
+func (m *manager) SendReaction(channelID *id.ID, reaction string,
+	replyTo cryptoChannel.MessageID, validUntil time.Duration,
+	params cmix.CMIXParams) (cryptoChannel.MessageID, id.Round, ephemeral.Id,
+	error) {
+
+	if len(reaction) != 1 {
+		return error
+	}
+
+	txt := &CMIXChannelReaction{
+		Version:           cmixChannelReactionVersion,
+		Reaction:          reaction,
+		ReactionMessageID: replyTo[:],
+	}
+
+	txtMarshaled, err := proto.Marshal(txt)
+	if err != nil {
+		return cryptoChannel.MessageID{}, 0, ephemeral.Id{}, err
+	}
+
+	return m.SendGeneric(channelID, txtMarshaled, validUntil, Text, params)
+}
diff --git a/go.mod b/go.mod
index c783049e4a394de789f9bd4d72ff01cd7fc25eb6..4864ab7a9547d35ab4967a01dcce25428fe867f0 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.17
 
 require (
 	github.com/cloudflare/circl v1.2.0
+	github.com/forPelevin/gomoji v1.1.6
 	github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
 	github.com/golang/protobuf v1.5.2
 	github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
@@ -39,6 +40,7 @@ require (
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/pelletier/go-toml v1.9.5 // indirect
 	github.com/pelletier/go-toml/v2 v2.0.2 // indirect
+	github.com/rivo/uniseg v0.3.4 // indirect
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
 	github.com/spf13/afero v1.9.2 // indirect
 	github.com/spf13/cast v1.5.0 // indirect
diff --git a/go.sum b/go.sum
index 15026f41da3db123c76448fdd513095d108f0814..3898f13b2d9af37a8e82268407d8423e1badb09a 100644
--- a/go.sum
+++ b/go.sum
@@ -123,6 +123,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
 github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
+github.com/forPelevin/gomoji v1.1.6 h1:mSIGhjyMiywuGFHR/6CLL/L6HwwDiQmYGdl1R9a/05w=
+github.com/forPelevin/gomoji v1.1.6/go.mod h1:h31zCiwG8nIto/c9RmijODA1xgN2JSvwKfU7l65xeTk=
 github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
 github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -358,6 +360,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
 github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
 github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
 github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw=
+github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=