P 2 Pchatservice
P 2 Pchatservice
We're the yellow node on the lower right. Yellow nodes are participants of a chatroom.
This is how the network looks like after the node requests a chatroom. All the nodes in the chatroom are now connected directly and separately from the Kademlia network.
bootstrapOnly bool
storeIdentity bool
ps *pubsub.PubSub
roomManager *RoomManager
eventPublishers []events.Publisher
eventPublishersLock sync.RWMutex
nicknameStoreMutex sync.Mutex
nicknameStoreWaiting bool
}
a)
message PingRequest {}
message PingResponse {}
b)
message SendMessageRequest {
string room_name = 1;
string value = 2;
}
message SendMessageResponse {
bool sent = 1;
}
c)
message ChatMessage {
string sender_id = 1;
int64 timestamp = 2;
string value = 3;
}
d)
message ModeratorMessage {
string sender_id = 1;
int64 timestamp = 2;
string value = 3;
}
e)
message BlockMessage {
string sender_id = 1;
int64 timestamp = 2;
string value = 3;
}
f)
message BanMessage {
string sender_id = 1;
int64 timestamp = 2;
string value = 3;
}
g)
message RateLimitMessage {
string sender_id = 1;
int64 timestamp = 2;
string value = 3;
}
h)
message GetNodeIDRequest {}
message GetNodeIDResponse {
string id = 1;
}
i)
message SetNicknameRequest {
string room_name = 1;
string nickname = 2;
}
message SetNicknameResponse {}
j)
message GetNicknameRequest {
string room_name = 1;
string peer_id = 2;
}
message GetNicknameResponse {
string nickname = 1;
}
k)
message JoinRoomRequest {
string room_name = 1;
string nickname = 2;
}
message JoinRoomResponse {}
l)
message RoomParticipant {
string id = 1;
string nickname = 2;
}
message GetRoomParticipantsRequest {
string room_name = 1;
}
message GetRoomParticipantsResponse {
repeated RoomParticipant participants = 1;
}
// Room holds room event and pubsub data.
type Room struct {
name string
topic *pubsub.Topic
subscription *pubsub.Subscription
// Map is counter of moderation messages for user in a chat room
usermoderationCounter sync.Map
ratelimitstore ratelimit.Store
lock sync.RWMutex
participants map[peer.ID]*participantsEntry
}
// RoomManager manages rooms through pubsub subscription and implements room operations.
type RoomManager struct {
logger *zap.Logger
ps *pubsub.PubSub
node Node
kadDHT *dht.IpfsDHT
bannedIPlist *block.Blocklist
moderatorConfig *vec.Model
rooms map[string]*Room
eventPublisher events.Publisher
lock sync.RWMutex
}
Architecture
// SendChatMessage sends a chat message to a given room.
// Fails if it has not yet joined the given room.
func (r *RoomManager) SendChatMessage(ctx context.Context, roomName string, msg
entities.Message) error {
room, found := r.getRoom(roomName)
if !found {
return errors.New(fmt.Sprintf("must join the room before sending messages"))
}
rm := &RoomMessageOut{
Type: RoomMessageTypeChatMessage,
Payload: msg,
}
return nil
}
After receiving message from subscription channel, it is unmarshalled in room message input,
message category is derived.
if subMsg.ReceivedFrom == r.node.ID() {
continue
}
var rm RoomMessageIn
if err := json.Unmarshal(subMsg.Data, &rm); err != nil {
r.logger.Warn("ignoring room message", zap.Error(err))
}
Messaging Layer
1)New Chat Message
2)New Moderate Message
3)New Block Message
4)New Ban Message
5)New Rate limit Message
6)New Advertising Message
7)Reaction Message
There is handler for each different type of message.
When message is received -> different Algorithms play a role as illustrated below -
If p2p message is coming from one of the Banned IP Addresses, then peer is banned and message
from this peer will automatically be dropped.
If user is posting too many messages in short period, user is rate limited using token bucket
Algorithm as per cooling period algorithm.
After analysing message – following categories are determined which need moderation
Negative Categories
1) Spam
2) Abuse
3) Adult content
Positive Categories
1)Highly relevant – quality content
2)Less relevant
3)Fun messages
Categories
1)Inactivity
2)Unblock
3)Unban
Different Maps are maintained for Normal Participants, Blocked Participants, Banned Participants.
If user is inactive for roomParticipantsTTL, user will automatically be evicted from the topic room.
If user is in blocked participant list and block ttl has expired, user will automatically be unblocked.
If user is in banned participant list and banned ttl has expired, user will automatically be unbanned.
User Message history and P2P chat session storage and recovery/resumption in peers
Bolt db is used for serialisation of p2p chat session and p2p chat session resumption in case peer
goes offline temporarily.
Bolt DB Key value specification
Geography, Internet bandwidth is derived from IP Address present in node boostrapped address
using maxmind databases for classfying peer in normal and light (very limited frequency) peer.
User content categories and User Personality are derived using saved user chat session data using
ML models.
Average user chat session length derived from serialisation of user chat history session in bold
db,helps determined user chat engagement time.
Bold db database is serialised in master data store like elasticsearch for analysing user frequency
category like new, returning, loyal (friends category) and analysing different chat sessions in which
different nodes participated for forming user graph.
These all properties can be used to assign badges to user as give in discord.
Event Driven Architecture
For each API called in GRPC node, events are emitted for main operations, all these events can be
sent to event tracker urls hosted by p2p chat service servers for storing event information and for
plugging this data in notification system (social network eg facebook notification), analytics system
and more.
message SubscribeToEventsRequest {}
// Events
message EvtNewChatMessage {
ChatMessage chat_message = 1;
string room_name = 2;
}
message EvtNewModeratorMessage {
ModeratorMessage moderator_message = 1;
string room_name = 2;
}
message EvtNewBlockMessage {
BlockMessage block_message = 1;
string room_name = 2;
}
message EvtNewBanMessage {
BanMessage ban_message = 1;
string room_name = 2;
}
message EvtNewRateLimitMessage {
RateLimitMessage rate_limit_message = 1;
string room_name = 2;
}
message EvtPeerJoined {
string room_name = 1;
string peer_id = 2;
}
message EvtPeerLeft {
string room_name = 1;
string peer_id = 2;
}
message EvtModerationRemoved {
string room_name = 1;
string peer_id = 2;
}
message EvtSetNickname {
string room_name = 1;
string peer_id = 2;
string nickname = 3;
}
Longer running Publish and Subscribe go routines i.e Pumps are started for publishing and
subscribing to topic in chatrooms.
As they are longer running they can be optimised using heartbeats and healing go routines
concurrency pattern.
default:
// Read a message from the subscription
message, err := cr.psub.Next(cr.psctx)
// Check error
if err != nil {
// Close the messages queue (subscription has closed)
close(cr.Inbound)
cr.Logs <- chatlog{logprefix: "suberr", logmsg: "subscription has
closed"}
return
}
// Declare a ChatMessage
cm := &chatmessage{}
// Unmarshal the message data into a ChatMessage
err = json.Unmarshal(message.Data, cm)
if err != nil {
cr.Logs <- chatlog{logprefix: "suberr", logmsg: "could not unmarshal
JSON"}
continue
}
A Kademlia DHT is then bootstrapped on this host using the default peers offered by libp2p
and a Peer Discovery service is created from this Kademlia DHT. The PubSub handler is then
created on the host using the peer discovery service created prior.
for {
<-tick
func() {
r.lock.RLock()
defer r.lock.RUnlock()
//In case advertisement message illustrated below from a user to chat on a topic in chatroom is not
coming for a specified ttl, then user will be evicted from chat participants list and participants map
will be refreshed.
rm := RoomMessageOut{
Type: RoomMessageTypeAdvertise,
Payload: thisNickname,
}