Merge pull request #32 from status-im/contact

Contact management
This commit is contained in:
Adrià Cidre 2018-06-15 09:29:08 +02:00 committed by GitHub
commit 537a851a94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 160 additions and 24 deletions

View File

@ -12,6 +12,7 @@ type Account struct {
PubKey string PubKey string
Mnemonic string Mnemonic string
Username string Username string
Image string
channels []*Channel channels []*Channel
} }
@ -39,19 +40,58 @@ func (a *Account) createAndJoin(name, password string) (*Channel, error) {
return a.Join(name, topicID, symKey) return a.Join(name, topicID, symKey)
} }
// OnContactRequest executes specified ContactRequestHandler logic when a contact
// request is received
func (a *Account) OnContactRequest(fn ContactRequestHandler) error {
topicID, err := a.calculatePublicChannelTopicID(discoveryChannelName)
if err != nil {
return err
}
filterID, err := newShhMessageFilterFormatRequest(a.conn, []string{topicID}, "", a.PubKey)
if err != nil {
println(err.Error())
return err
}
discoveryChannel := &Channel{
account: a,
name: discoveryChannelName,
filterID: filterID,
TopicID: topicID,
ChannelPubKey: a.PubKey,
}
_, err = discoveryChannel.Subscribe(func(msg *Msg) {
switch c := msg.Properties.(type) {
case *NewContactKeyMsg:
fn(&Contact{
account: a,
SymKey: c.Address,
TopicID: c.Topic,
Name: c.Contact.Name,
Address: c.Contact.Address,
Image: c.Contact.Image,
})
}
})
return err
}
// Join joins a status channel // Join joins a status channel
func (a *Account) Join(channelName, topicID, symKey string) (*Channel, error) { func (a *Account) Join(channelName, topicID, symKey string) (*Channel, error) {
filterID, err := newShhMessageFilterFormatRequest(a.conn, []string{topicID}, symKey) filterID, err := newShhMessageFilterFormatRequest(a.conn, []string{topicID}, symKey, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
ch := &Channel{ ch := &Channel{
account: a, account: a,
name: channelName, name: channelName,
filterID: filterID, filterID: filterID,
TopicID: topicID, TopicID: topicID,
ChannelKey: symKey, ChannelSymKey: symKey,
} }
a.channels = append(a.channels, ch) a.channels = append(a.channels, ch)

11
chan.go
View File

@ -7,12 +7,15 @@ import (
"time" "time"
) )
const discoveryChannelName = "contact-discovery"
// Channel : ... // Channel : ...
type Channel struct { type Channel struct {
account *Account account *Account
name string name string
filterID string filterID string
ChannelKey string ChannelSymKey string
ChannelPubKey string
TopicID string TopicID string
visibility string visibility string
subscriptions []*Subscription subscriptions []*Subscription
@ -69,9 +72,9 @@ func (c *Channel) ContactRequest(username, image string) error {
// contact accepts the contact request. It will be sent on the topic that // contact accepts the contact request. It will be sent on the topic that
// was provided in the NewContactKey message and use the sym-key. // was provided in the NewContactKey message and use the sym-key.
// Both users will therefore have the same filter. // Both users will therefore have the same filter.
func (c *Channel) ConfirmedContactRequest(username, image string) error { func (c *Channel) ConfirmedContactRequest(ct *Contact) error {
format := `["%s",["%s","%s","%s","%s"]]` format := `["%s",["%s","%s","%s","%s"]]`
msg := fmt.Sprintf(format, ConfirmedContactRequestType, username, image, c.account.Address, "") msg := fmt.Sprintf(format, ConfirmedContactRequestType, c.account.Username, c.account.Image, c.account.Address, "")
return c.SendPostRawMsg(msg) return c.SendPostRawMsg(msg)
} }
@ -118,7 +121,7 @@ func (c *Channel) ContactUpdateRequest(username, image string) error {
func (c *Channel) SendPostRawMsg(body string) error { func (c *Channel) SendPostRawMsg(body string) error {
msg := Message{ msg := Message{
Signature: c.account.AddressKeyID, Signature: c.account.AddressKeyID,
SymKeyID: c.ChannelKey, SymKeyID: c.ChannelSymKey,
Payload: rawrChatMessage(body), Payload: rawrChatMessage(body),
Topic: c.TopicID, Topic: c.TopicID,
TTL: 10, TTL: 10,

30
contact.go Normal file
View File

@ -0,0 +1,30 @@
package sdk
// Contact details for a known user
type Contact struct {
account *Account
ch *Channel
SymKey string
TopicID string
Name string
Image string
Address string
}
// Accept accepts a contact as a friend, sends back a ConfirmedContactRequest
func (c *Contact) Accept() error {
// Add the proposed symkey
symkey, err := addSymKey(c.account.conn, c.SymKey)
if err != nil {
return err
}
c.ch, err = c.account.Join(c.Name, c.TopicID, symkey)
if err != nil {
return err
}
return c.ch.ConfirmedContactRequest(c)
}
// ContactRequestHandler handler for contact requests
type ContactRequestHandler func(*Contact)

View File

@ -7,20 +7,28 @@ func shhGenerateSymKeyFromPasswordRequest(sdk *SDK, password string) (string, er
} }
type shhFilterFormatParam struct { type shhFilterFormatParam struct {
AllowP2P bool `json:"allowP2P"` AllowP2P bool `json:"allowP2P"`
Topics []string `json:"topics"` Topics []string `json:"topics"`
Type string `json:"type"` Type string `json:"type"`
SymKeyID string `json:"symKeyID"` SymKeyID string `json:"symKeyID"`
PrivateKeyID string `json:"privateKeyID"`
} }
func newShhMessageFilterFormatRequest(sdk *SDK, topics []string, symKey string) (string, error) { func newShhMessageFilterFormatRequest(sdk *SDK, topics []string, symKey, privateKeyID string) (string, error) {
// `{"jsonrpc":"2.0","id":2,"method":"shh_newMessageFilter","params":[{"allowP2P":true,"topics":["%s"],"type":"sym","symKeyID":"%s"}]}` // `{"jsonrpc":"2.0","id":2,"method":"shh_newMessageFilter","params":[{"allowP2P":true,"topics":["%s"],"type":"sym","symKeyID":"%s"}]}`
var res string var res string
params := &shhFilterFormatParam{ params := &shhFilterFormatParam{
AllowP2P: true, AllowP2P: true,
Topics: topics, Topics: topics,
Type: "sym", Type: "sym",
SymKeyID: symKey, SymKeyID: symKey,
PrivateKeyID: privateKeyID,
}
if len(symKey) > 0 {
params.SymKeyID = symKey
}
if len(privateKeyID) > 0 {
params.PrivateKeyID = privateKeyID
} }
return res, sdk.RPCClient.Call(&res, "shh_newMessageFilter", params) return res, sdk.RPCClient.Call(&res, "shh_newMessageFilter", params)
@ -83,7 +91,8 @@ func shhGetFilterMessagesRequest(sdk *SDK, filter string) (interface{}, error) {
// Message message to be sent though ssh_post calls // Message message to be sent though ssh_post calls
type Message struct { type Message struct {
Signature string `json:"sig"` Signature string `json:"sig"`
SymKeyID string `json:"symKeyID"` SymKeyID string `json:"symKeyID,omitempty"`
PubKey string `json:"pubKey,omitempty"`
Payload string `json:"payload"` Payload string `json:"payload"`
Topic string `json:"topic"` Topic string `json:"topic"`
TTL uint32 `json:"ttl"` TTL uint32 `json:"ttl"`
@ -93,5 +102,11 @@ type Message struct {
func shhPostRequest(sdk *SDK, msg *Message) (string, error) { func shhPostRequest(sdk *SDK, msg *Message) (string, error) {
var res string var res string
return res, sdk.RPCClient.Call(&res, "shh_post", msg) return res, sdk.RPCClient.Call(&res, "shhext_post", msg)
}
func addSymKey(sdk *SDK, symKey string) (string, error) {
// {"jsonrpc":"2.0","method":"shh_addSymKey", "params":["` + symkey + `"], "id":1}
var res string
return res, sdk.RPCClient.Call(&res, "shh_addSymKey", symKey)
} }

View File

@ -59,7 +59,7 @@ func TestDictionaryMethods(t *testing.T) {
{ {
Description: "shhPostRequest", Description: "shhPostRequest",
Response: &str, Response: &str,
Method: "shh_post", Method: "shhext_post",
Params: &Message{}, Params: &Message{},
Callback: func(sdk *SDK) { Callback: func(sdk *SDK) {
response, err := shhPostRequest(sdk, &Message{}) response, err := shhPostRequest(sdk, &Message{})
@ -100,7 +100,7 @@ func TestDictionaryMethods(t *testing.T) {
SymKeyID: "", SymKeyID: "",
}, },
Callback: func(sdk *SDK) { Callback: func(sdk *SDK) {
response, err := newShhMessageFilterFormatRequest(sdk, []string{}, "") response, err := newShhMessageFilterFormatRequest(sdk, []string{}, "", "")
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, response) assert.NotNil(t, response)
}, },

48
examples/contact/main.go Normal file

File diff suppressed because one or more lines are too long

2
msg.go
View File

@ -166,7 +166,7 @@ type ContactMsg struct {
} }
func contactMsgFromProperties(properties []interface{}) *ContactMsg { func contactMsgFromProperties(properties []interface{}) *ContactMsg {
crProperties := properties[2].([]interface{}) crProperties := properties[1].([]interface{})
return &ContactMsg{ return &ContactMsg{
Name: crProperties[0].(string), Name: crProperties[0].(string),