status-go-sdk/chan.go

213 lines
6.4 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 sdk
import (
"encoding/json"
"fmt"
"log"
"time"
)
// Channel : ...
type Channel struct {
account *Account
name string
filterID string
ChannelKey string
TopicID string
visibility string
subscriptions []*Subscription
}
// Subscribe to the current channel by polling the network for new messages
// and executing provided handler
func (c *Channel) Subscribe(fn MsgHandler) (*Subscription, error) {
log.Println("Subscribed to channel '", c.name, "'")
subscription := &Subscription{}
go subscription.Subscribe(c, fn)
c.subscriptions = append(c.subscriptions, subscription)
return subscription, nil
}
// Close current channel and all its subscriptions
func (c *Channel) Close() {
for _, sub := range c.subscriptions {
c.removeSubscription(sub)
}
}
// NewContactKeyRequest first message that is sent to a future contact. At that
// point the only topic we know that the contact is filtering is the
// discovery-topic with his public key so that is what NewContactKey will
// be sent to.
// It contains the sym-key and topic that will be used for future communications
// as well as the actual message that we want to send.
// The sym-key and topic are generated randomly because we dont want to have
// any correlation between a topic and its participants to avoid leaking
// metadata.
// When one of the contacts recovers his account, a NewContactKey message is
// sent as well to change the symmetric key and topic.
func (c *Channel) NewContactKeyRequest(username string) error {
format := `["%s",["%s","%s","%s","%s"]]]`
contactRequest := fmt.Sprintf(format, ContactRequestType, username, "", "", "")
format = `["%s",["%s","%s",%s]`
msg := fmt.Sprintf(format, NewContactKeyType, c.account.Address, c.TopicID, contactRequest)
return c.SendPostRawMsg(msg)
}
// ContactRequest wrapped in a NewContactKey message when initiating a contact request.
func (c *Channel) ContactRequest(username, image string) error {
format := `["%s",["%s","%s","%s","%s"]]]`
msg := fmt.Sprintf(format, ContactRequestType, username, image, c.account.Address, "")
return c.SendPostRawMsg(msg)
}
// ConfirmedContactRequest this is the message that will be sent when the
// contact accepts the contact request. It will be sent on the topic that
// was provided in the NewContactKey message and use the sym-key.
// Both users will therefore have the same filter.
func (c *Channel) ConfirmedContactRequest(username, image string) error {
format := `["%s",["%s","%s","%s","%s"]]`
msg := fmt.Sprintf(format, ConfirmedContactRequestType, username, image, c.account.Address, "")
return c.SendPostRawMsg(msg)
}
// Publish a message with the given body on the current channel
func (c *Channel) Publish(body string) error {
visibility := "~:public-group-user-message"
if c.visibility != "" {
visibility = c.visibility
}
now := time.Now().Unix() * 1000
format := `["%s",["%s","text/plain","%s",%d,%d]]`
msg := fmt.Sprintf(format, StandardMessageType, body, visibility, now*100, now)
println("[ SENDING ] : " + msg)
return c.SendPostRawMsg(msg)
}
// SeenRequest sent when a user sees a message (opens the chat and loads the
// message). Can acknowledge multiple messages at the same time
func (c *Channel) SeenRequest(ids []string) error {
format := `["%s",["%s","%s"]]`
body, err := json.Marshal(ids)
if err != nil {
return err
}
msg := fmt.Sprintf(format, SeenType, body)
return c.SendPostRawMsg(msg)
}
// ContactUpdateRequest sent when the user changes his name or profile-image.
func (c *Channel) ContactUpdateRequest(username, image string) error {
format := `["%s",["%s","%s"]]`
msg := fmt.Sprintf(format, ContactUpdateType, username, image)
return c.SendPostRawMsg(msg)
}
// SendPostRawMsg sends a shh_post message with the given body.
func (c *Channel) SendPostRawMsg(body string) error {
msg := Message{
Signature: c.account.Address,
SymKeyID: c.ChannelKey,
Payload: rawrChatMessage(body),
Topic: c.TopicID,
TTL: 10,
PowTarget: c.account.conn.minimumPoW,
PowTime: 1,
}
_, err := shhPostRequest(c.account.conn, &msg)
if err != nil {
log.Println(err.Error())
}
return err
}
// PNBroadcastAvailabilityRequest makes a request used by push notification
// servers to broadcast its availability, this request is exposing current
// push notification server Public Key.
func (c *Channel) PNBroadcastAvailabilityRequest() error {
format := `["%s",["%s"]]`
msg := fmt.Sprintf(format, PNBroadcastAvailabilityType, c.account.PubKey)
return c.SendPostRawMsg(msg)
}
// PNRegistrationRequest request sent by clients wanting to be registered on
// a specific push notification server.
// The client has to provide a channel(topic + symkey) so the future
// communications happen through this channel.
// Additionally a device token will identify the device on the push notification
// provider.
func (c *Channel) PNRegistrationRequest(symkey, topic, deviceToken string, slotAvailabilityRatio float32) error {
format := `["%s",["%s","%s","%s", %g]]`
msg := fmt.Sprintf(format, PNRegistrationType, symkey, topic, deviceToken, slotAvailabilityRatio)
return c.SendPostRawMsg(msg)
}
// PNRegistrationConfirmationRequest request sent by the push notification
// server to let a client know what's the pubkey associated with its registered
// token.
func (c *Channel) PNRegistrationConfirmationRequest(pubkey string) error {
format := `["%s",["%s"]]`
msg := fmt.Sprintf(format, PNRegistrationConfirmationType, pubkey)
return c.SendPostRawMsg(msg)
}
func (c *Channel) removeSubscription(sub *Subscription) {
var subs []*Subscription
for _, s := range c.subscriptions {
if s != sub {
subs = append(subs, s)
}
}
c.subscriptions = subs
}
// PubKey return the channel's associated publike key
func (c *Channel) PubKey() string {
return c.account.PubKey
}
func (c *Channel) pollMessages() (msg *Msg) {
res, err := shhGetFilterMessagesRequest(c.account.conn, c.filterID)
if err != nil {
log.Fatalf("Error when sending request to server: %s", err)
return
}
switch vv := res.(type) {
case []interface{}:
for _, u := range vv {
msg, err = messageFromEnvelope(u)
if err == nil && supportedMessage(msg.Type) {
msg.Channel = c
msg.ChannelName = c.name
return
} else if err != nil {
log.Println("[ ERROR ]", err.Error())
return
} else {
log.Println("[ ERROR ]", "Invalid message type", msg.Type)
}
return nil
}
default:
log.Println(res, "is of a type I don't know how to handle")
}
return
}