[TASK] add support to receive image from threema
This commit is contained in:
parent
211e0abed0
commit
b9bf07389c
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
type Account struct {
|
type Account struct {
|
||||||
models.AccountThreema
|
models.AccountThreema
|
||||||
|
threema *Threema
|
||||||
Session o3.SessionContext
|
Session o3.SessionContext
|
||||||
send chan<- o3.Message
|
send chan<- o3.Message
|
||||||
receive <-chan o3.ReceivedMsg
|
receive <-chan o3.ReceivedMsg
|
||||||
|
@ -43,8 +44,11 @@ func (t *Threema) getAccount(jid *models.JID) (*Account, error) {
|
||||||
}
|
}
|
||||||
tid.Nick = o3.NewPubNick("xmpp:" + jid.String())
|
tid.Nick = o3.NewPubNick("xmpp:" + jid.String())
|
||||||
|
|
||||||
a := &Account{AccountThreema: account}
|
a := &Account{
|
||||||
a.Session = o3.NewSessionContext(tid)
|
AccountThreema: account,
|
||||||
|
Session: o3.NewSessionContext(tid),
|
||||||
|
threema: t,
|
||||||
|
}
|
||||||
a.send, a.receive, err = a.Session.Run()
|
a.send, a.receive, err = a.Session.Run()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package threema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"gosrc.io/xmpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *Account) FileToXMPP(from string, msgID uint64, ext string, data []byte) (xmpp.Message, error) {
|
||||||
|
msgIDStr := strconv.FormatUint(msgID, 10)
|
||||||
|
msg := xmpp.Message{
|
||||||
|
PacketAttrs: xmpp.PacketAttrs{
|
||||||
|
Id: msgIDStr,
|
||||||
|
From: from,
|
||||||
|
To: a.XMPP.String(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
url := fmt.Sprintf("%s/%d.%s", a.threema.httpUploadURL, msgID, ext)
|
||||||
|
path := fmt.Sprintf("%s/%d.%s", a.threema.httpUploadPath, msgID, ext)
|
||||||
|
if err := ioutil.WriteFile(path, data, 0644); err != nil {
|
||||||
|
msg.Body = "unable to save file on transport to forward"
|
||||||
|
return msg, err
|
||||||
|
}
|
||||||
|
msg.Body = url
|
||||||
|
msg.X = &xmpp.MsgXOOB{URL: url}
|
||||||
|
return msg, nil
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package threema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFileToXMPP(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
a := createDummyAccount()
|
||||||
|
a.threema = &Threema{
|
||||||
|
httpUploadURL: "a",
|
||||||
|
httpUploadPath: "/tmp",
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := a.FileToXMPP("", 1, "jpg", []byte("hallo"))
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal("a/1.jpg", msg.X.URL)
|
||||||
|
|
||||||
|
a.threema.httpUploadPath = "/gibt/es/nicht"
|
||||||
|
msg, err = a.FileToXMPP("", 1, "jpg", []byte("hallo"))
|
||||||
|
assert.Error(err)
|
||||||
|
assert.Equal("unable to save file on transport to forward", msg.Body)
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package threema
|
package threema
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/bdlm/log"
|
"github.com/bdlm/log"
|
||||||
|
@ -17,14 +18,31 @@ type Threema struct {
|
||||||
out chan xmpp.Packet
|
out chan xmpp.Packet
|
||||||
accountJID map[string]*Account
|
accountJID map[string]*Account
|
||||||
bot map[string]*Bot
|
bot map[string]*Bot
|
||||||
|
httpUploadPath string
|
||||||
|
httpUploadURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewThreema(config map[string]interface{}) (component.Component, error) {
|
func NewThreema(config map[string]interface{}) (component.Component, error) {
|
||||||
return &Threema{
|
t := &Threema{
|
||||||
out: make(chan xmpp.Packet),
|
out: make(chan xmpp.Packet),
|
||||||
accountJID: make(map[string]*Account),
|
accountJID: make(map[string]*Account),
|
||||||
bot: make(map[string]*Bot),
|
bot: make(map[string]*Bot),
|
||||||
}, nil
|
}
|
||||||
|
if pathI, ok := config["http_upload_path"]; ok {
|
||||||
|
if path, ok := pathI.(string); ok {
|
||||||
|
t.httpUploadPath = path
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("wrong format of http_upload_path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if urlI, ok := config["http_upload_url"]; ok {
|
||||||
|
if url, ok := urlI.(string); ok {
|
||||||
|
t.httpUploadURL = url
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("wrong format of http_upload_url")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Threema) Connect() (chan xmpp.Packet, error) {
|
func (t *Threema) Connect() (chan xmpp.Packet, error) {
|
||||||
|
|
|
@ -13,7 +13,25 @@ import (
|
||||||
func TestThreema(t *testing.T) {
|
func TestThreema(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
c, err := NewThreema(map[string]interface{}{})
|
// failed
|
||||||
|
c, err := NewThreema(map[string]interface{}{
|
||||||
|
"http_upload_path": 3,
|
||||||
|
})
|
||||||
|
assert.Error(err)
|
||||||
|
assert.Nil(c)
|
||||||
|
|
||||||
|
// failed
|
||||||
|
c, err = NewThreema(map[string]interface{}{
|
||||||
|
"http_upload_url": 3,
|
||||||
|
})
|
||||||
|
assert.Error(err)
|
||||||
|
assert.Nil(c)
|
||||||
|
|
||||||
|
// ---
|
||||||
|
c, err = NewThreema(map[string]interface{}{
|
||||||
|
"http_upload_url": "",
|
||||||
|
"http_upload_path": "",
|
||||||
|
})
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.NotNil(c)
|
assert.NotNil(c)
|
||||||
|
|
||||||
|
@ -75,6 +93,14 @@ func TestSend(t *testing.T) {
|
||||||
p = tr.send(xmpp.IQ{})
|
p = tr.send(xmpp.IQ{})
|
||||||
assert.Nil(p)
|
assert.Nil(p)
|
||||||
|
|
||||||
|
// chat with bot without sender
|
||||||
|
p = tr.send(xmpp.Message{
|
||||||
|
PacketAttrs: xmpp.PacketAttrs{
|
||||||
|
To: "example.org",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert.Nil(p)
|
||||||
|
|
||||||
// chat with bot
|
// chat with bot
|
||||||
p = tr.send(xmpp.Message{
|
p = tr.send(xmpp.Message{
|
||||||
PacketAttrs: xmpp.PacketAttrs{
|
PacketAttrs: xmpp.PacketAttrs{
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package threema
|
package threema
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/bdlm/log"
|
"github.com/bdlm/log"
|
||||||
|
@ -10,27 +12,55 @@ import (
|
||||||
|
|
||||||
func (a *Account) receiver(out chan<- xmpp.Packet) {
|
func (a *Account) receiver(out chan<- xmpp.Packet) {
|
||||||
for receivedMessage := range a.receive {
|
for receivedMessage := range a.receive {
|
||||||
if p := a.receiving(receivedMessage); p != nil {
|
sender := receivedMessage.Msg.Sender().String()
|
||||||
|
if string(a.TID) == sender {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p, err := a.receiving(receivedMessage); err != nil {
|
||||||
|
xMSG := xmpp.NewMessage("chat", sender, a.XMPP.String(), "", "en")
|
||||||
|
xMSG.Body = fmt.Sprintf("error on decoding message: %s\n%v", err, receivedMessage.Msg.Serialize())
|
||||||
|
out <- xMSG
|
||||||
|
} else if p != nil {
|
||||||
out <- p
|
out <- p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (a *Account) receiving(receivedMessage o3.ReceivedMsg) xmpp.Packet {
|
|
||||||
|
func requestExtensions(xMSG *xmpp.Message) {
|
||||||
|
xMSG.Extensions = append(xMSG.Extensions, xmpp.ReceiptRequest{})
|
||||||
|
xMSG.Extensions = append(xMSG.Extensions, xmpp.ChatMarkerMarkable{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Account) receiving(receivedMessage o3.ReceivedMsg) (xmpp.Packet, error) {
|
||||||
if receivedMessage.Err != nil {
|
if receivedMessage.Err != nil {
|
||||||
log.Warnf("Error Receiving Message: %s\n", receivedMessage.Err)
|
log.Warnf("Error Receiving Message: %s\n", receivedMessage.Err)
|
||||||
return nil
|
return nil, receivedMessage.Err
|
||||||
}
|
}
|
||||||
switch msg := receivedMessage.Msg.(type) {
|
switch msg := receivedMessage.Msg.(type) {
|
||||||
case o3.TextMessage:
|
case o3.TextMessage:
|
||||||
sender := msg.Sender().String()
|
sender := msg.Sender().String()
|
||||||
if string(a.TID) == sender {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
xMSG := xmpp.NewMessage("chat", sender, a.XMPP.String(), strconv.FormatUint(msg.ID(), 10), "en")
|
xMSG := xmpp.NewMessage("chat", sender, a.XMPP.String(), strconv.FormatUint(msg.ID(), 10), "en")
|
||||||
xMSG.Body = msg.Text()
|
xMSG.Body = msg.Text()
|
||||||
xMSG.Extensions = append(xMSG.Extensions, xmpp.ReceiptRequest{})
|
requestExtensions(&xMSG)
|
||||||
xMSG.Extensions = append(xMSG.Extensions, xmpp.ChatMarkerMarkable{})
|
return xMSG, nil
|
||||||
return xMSG
|
|
||||||
|
case o3.ImageMessage:
|
||||||
|
if a.threema.httpUploadPath == "" {
|
||||||
|
return nil, errors.New("no place to store files at transport configurated")
|
||||||
|
}
|
||||||
|
data, err := msg.GetImageData(a.Session)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("unable to read data from message: %s", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
xMSG, err := a.FileToXMPP(msg.Sender().String(), msg.ID(), "jpg", data)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("unable to create data from message: %s", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
xMSG.Type = "chat"
|
||||||
|
requestExtensions(&xMSG)
|
||||||
|
return xMSG, nil
|
||||||
|
|
||||||
case o3.DeliveryReceiptMessage:
|
case o3.DeliveryReceiptMessage:
|
||||||
msgID := msg.MsgID()
|
msgID := msg.MsgID()
|
||||||
|
@ -55,8 +85,9 @@ func (a *Account) receiving(receivedMessage o3.ReceivedMsg) xmpp.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(xMSG.Extensions) > 0 {
|
if len(xMSG.Extensions) > 0 {
|
||||||
return xMSG
|
return xMSG, nil
|
||||||
}
|
}
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil, errors.New("not known data format")
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,14 +34,16 @@ func TestReceive(t *testing.T) {
|
||||||
a := createDummyAccount()
|
a := createDummyAccount()
|
||||||
|
|
||||||
// receiving/skip error
|
// receiving/skip error
|
||||||
p := a.receiving(o3.ReceivedMsg{
|
p, err := a.receiving(o3.ReceivedMsg{
|
||||||
Err: errors.New("dummy"),
|
Err: errors.New("dummy"),
|
||||||
})
|
})
|
||||||
assert.Nil(p)
|
assert.Nil(p)
|
||||||
|
assert.Error(err)
|
||||||
|
|
||||||
// nothing to receiving
|
// nothing to receiving
|
||||||
p = a.receiving(o3.ReceivedMsg{})
|
p, err = a.receiving(o3.ReceivedMsg{})
|
||||||
assert.Nil(p)
|
assert.Nil(p)
|
||||||
|
assert.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReceiveText(t *testing.T) {
|
func TestReceiveText(t *testing.T) {
|
||||||
|
@ -58,26 +60,40 @@ func TestReceiveText(t *testing.T) {
|
||||||
}
|
}
|
||||||
txtMsg, err := o3.NewTextMessage(&session, threemaID, "Oojoh0Ah")
|
txtMsg, err := o3.NewTextMessage(&session, threemaID, "Oojoh0Ah")
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
p := a.receiving(o3.ReceivedMsg{
|
p, err := a.receiving(o3.ReceivedMsg{
|
||||||
Msg: txtMsg,
|
Msg: txtMsg,
|
||||||
})
|
})
|
||||||
|
assert.NoError(err)
|
||||||
xMSG, ok := p.(xmpp.Message)
|
xMSG, ok := p.(xmpp.Message)
|
||||||
assert.True(ok)
|
assert.True(ok)
|
||||||
assert.Equal("Oojoh0Ah", xMSG.Body)
|
assert.Equal("Oojoh0Ah", xMSG.Body)
|
||||||
|
}
|
||||||
|
|
||||||
// receiving/skip text to own id
|
func TestReceiveImage(t *testing.T) {
|
||||||
session = o3.SessionContext{
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
a := createDummyAccount()
|
||||||
|
a.threema = &Threema{}
|
||||||
|
|
||||||
|
/* receiving image
|
||||||
|
session := o3.SessionContext{
|
||||||
ID: o3.ThreemaID{
|
ID: o3.ThreemaID{
|
||||||
ID: threemaIDByte,
|
ID: o3.NewIDString("12345678"),
|
||||||
Nick: o3.NewPubNick("user"),
|
Nick: o3.NewPubNick("user"),
|
||||||
},
|
},
|
||||||
}
|
}*/
|
||||||
txtMsg, err = o3.NewTextMessage(&session, threemaID, "Aesh8shu")
|
imgMsg := o3.ImageMessage{}
|
||||||
assert.NoError(err)
|
_, err := a.receiving(o3.ReceivedMsg{
|
||||||
p = a.receiving(o3.ReceivedMsg{
|
Msg: imgMsg,
|
||||||
Msg: txtMsg,
|
|
||||||
})
|
})
|
||||||
assert.Nil(p)
|
assert.Error(err)
|
||||||
|
|
||||||
|
a.threema.httpUploadPath = "/tmp"
|
||||||
|
imgMsg = o3.ImageMessage{}
|
||||||
|
_, err = a.receiving(o3.ReceivedMsg{
|
||||||
|
Msg: imgMsg,
|
||||||
|
})
|
||||||
|
assert.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReceiveDeliveryReceipt(t *testing.T) {
|
func TestReceiveDeliveryReceipt(t *testing.T) {
|
||||||
|
@ -98,34 +114,38 @@ func TestReceiveDeliveryReceipt(t *testing.T) {
|
||||||
|
|
||||||
drm, err := o3.NewDeliveryReceiptMessage(&session, threemaID, msgID, o3.MSGDELIVERED)
|
drm, err := o3.NewDeliveryReceiptMessage(&session, threemaID, msgID, o3.MSGDELIVERED)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
p := a.receiving(o3.ReceivedMsg{
|
p, err := a.receiving(o3.ReceivedMsg{
|
||||||
Msg: drm,
|
Msg: drm,
|
||||||
})
|
})
|
||||||
|
assert.NoError(err)
|
||||||
xMSG, ok := p.(xmpp.Message)
|
xMSG, ok := p.(xmpp.Message)
|
||||||
assert.True(ok)
|
assert.True(ok)
|
||||||
rr := xMSG.Extensions[0].(xmpp.ReceiptReceived)
|
rr := xMSG.Extensions[0].(xmpp.ReceiptReceived)
|
||||||
assert.Equal("im4aeseeh1IbaQui", rr.Id)
|
assert.Equal("im4aeseeh1IbaQui", rr.Id)
|
||||||
|
|
||||||
// receiving delivered -> not in cache
|
// receiving delivered -> not in cache
|
||||||
p = a.receiving(o3.ReceivedMsg{
|
p, err = a.receiving(o3.ReceivedMsg{
|
||||||
Msg: drm,
|
Msg: drm,
|
||||||
})
|
})
|
||||||
|
assert.NoError(err)
|
||||||
assert.Nil(p)
|
assert.Nil(p)
|
||||||
|
|
||||||
// receiving readed
|
// receiving readed
|
||||||
drm, err = o3.NewDeliveryReceiptMessage(&session, threemaID, msgID, o3.MSGREAD)
|
drm, err = o3.NewDeliveryReceiptMessage(&session, threemaID, msgID, o3.MSGREAD)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
p = a.receiving(o3.ReceivedMsg{
|
p, err = a.receiving(o3.ReceivedMsg{
|
||||||
Msg: drm,
|
Msg: drm,
|
||||||
})
|
})
|
||||||
|
assert.NoError(err)
|
||||||
xMSG, ok = p.(xmpp.Message)
|
xMSG, ok = p.(xmpp.Message)
|
||||||
assert.True(ok)
|
assert.True(ok)
|
||||||
cmdd := xMSG.Extensions[0].(xmpp.ChatMarkerDisplayed)
|
cmdd := xMSG.Extensions[0].(xmpp.ChatMarkerDisplayed)
|
||||||
assert.Equal("im4aeseeh1IbaQui", cmdd.Id)
|
assert.Equal("im4aeseeh1IbaQui", cmdd.Id)
|
||||||
|
|
||||||
// receiving delivered -> not in cache
|
// receiving delivered -> not in cache
|
||||||
p = a.receiving(o3.ReceivedMsg{
|
p, err = a.receiving(o3.ReceivedMsg{
|
||||||
Msg: drm,
|
Msg: drm,
|
||||||
})
|
})
|
||||||
|
assert.NoError(err)
|
||||||
assert.Nil(p)
|
assert.Nil(p)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,10 @@ host = "threema.chat.sum7.eu"
|
||||||
connection = "localhost:5347"
|
connection = "localhost:5347"
|
||||||
secret = "change_me"
|
secret = "change_me"
|
||||||
|
|
||||||
|
[component.special]
|
||||||
|
http_upload_url = "https://example.org/upload/threema"
|
||||||
|
http_upload_path = "/var/www/http/upload/threema"
|
||||||
|
|
||||||
[database]
|
[database]
|
||||||
type = "sqlite3"
|
type = "sqlite3"
|
||||||
logging = false
|
logging = false
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3ddad3f7a28604fa8c4fd5035d5a37aa1d676c95
|
Subproject commit d0b7b1f864f0fd4651f8a92b92a341314ef7aa5b
|
Reference in New Issue