Very spammy and buggy prototype.
parent
2a175e4bfc
commit
cd5696954c
@ -0,0 +1,84 @@
|
|||||||
|
package botcommands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
"github.com/hugot/go-deltachat/deltachat"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Database interface {
|
||||||
|
GetWhappJIDForDCID(DCID uint32) (*string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWhappBridge(
|
||||||
|
wac *whatsapp.Conn,
|
||||||
|
db Database,
|
||||||
|
UserChatID uint32,
|
||||||
|
) *WhappBridge {
|
||||||
|
return &WhappBridge{
|
||||||
|
wac: wac,
|
||||||
|
db: db,
|
||||||
|
UserChatID: UserChatID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type WhappBridge struct {
|
||||||
|
wac *whatsapp.Conn
|
||||||
|
db Database
|
||||||
|
UserChatID uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *WhappBridge) Accepts(c *deltachat.Chat, m *deltachat.Message) bool {
|
||||||
|
chatID := c.GetID()
|
||||||
|
|
||||||
|
chatJID, err := b.db.GetWhappJIDForDCID(chatID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// The database is failing, time to die :(
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return chatJID != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *WhappBridge) Execute(
|
||||||
|
c *deltachat.Context,
|
||||||
|
chat *deltachat.Chat,
|
||||||
|
m *deltachat.Message,
|
||||||
|
) {
|
||||||
|
JID, err := b.db.GetWhappJIDForDCID(chat.GetID())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.SendTextMessage(
|
||||||
|
b.UserChatID,
|
||||||
|
fmt.Sprintf(
|
||||||
|
"Whapp bridge dying: %s",
|
||||||
|
err.Error(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
text := whatsapp.TextMessage{
|
||||||
|
Info: whatsapp.MessageInfo{
|
||||||
|
RemoteJid: *JID,
|
||||||
|
},
|
||||||
|
Text: m.GetText(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = b.wac.Send(text)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.SendTextMessage(
|
||||||
|
b.UserChatID,
|
||||||
|
fmt.Sprintf(
|
||||||
|
"Error sending message to %s. Message contents: %s",
|
||||||
|
JID,
|
||||||
|
m.GetText(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"go.etcd.io/bbolt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Database struct {
|
||||||
|
db *bbolt.DB
|
||||||
|
dbPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
JID_TO_DCID_INT uint8 = iota
|
||||||
|
DCID_TO_JID_INT
|
||||||
|
KEY_VALUE_INT
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
JID_TO_DCID = []byte{JID_TO_DCID_INT}
|
||||||
|
DCID_TO_JID = []byte{DCID_TO_JID_INT}
|
||||||
|
KEY_VALUE = []byte{KEY_VALUE_INT}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Database) Init() error {
|
||||||
|
db, err := bbolt.Open(d.dbPath, 0600, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.Update(func(tx *bbolt.Tx) error {
|
||||||
|
_, err = tx.CreateBucketIfNotExists(JID_TO_DCID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.CreateBucketIfNotExists(DCID_TO_JID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.CreateBucketIfNotExists(KEY_VALUE)
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
d.db = db
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) GetDCIDForWhappJID(JID string) (*uint32, error) {
|
||||||
|
var DCID *uint32
|
||||||
|
|
||||||
|
err := d.db.View(func(tx *bbolt.Tx) error {
|
||||||
|
rawDCID := tx.Bucket(JID_TO_DCID).Get([]byte(JID))
|
||||||
|
|
||||||
|
if rawDCID == nil {
|
||||||
|
DCID = nil
|
||||||
|
} else {
|
||||||
|
i := binary.LittleEndian.Uint32(rawDCID)
|
||||||
|
DCID = &i
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return DCID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) GetWhappJIDForDCID(DCID uint32) (*string, error) {
|
||||||
|
var JID *string
|
||||||
|
|
||||||
|
rawDCID := make([]byte, 4)
|
||||||
|
binary.LittleEndian.PutUint32(rawDCID, DCID)
|
||||||
|
|
||||||
|
err := d.db.View(func(tx *bbolt.Tx) error {
|
||||||
|
|
||||||
|
rawJID := tx.Bucket(DCID_TO_JID).Get(rawDCID)
|
||||||
|
|
||||||
|
if rawJID == nil {
|
||||||
|
JID = nil
|
||||||
|
} else {
|
||||||
|
str := string(rawJID)
|
||||||
|
JID = &str
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return JID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) StoreDCIDForJID(JID string, DCID uint32) error {
|
||||||
|
err := d.db.Update(func(tx *bbolt.Tx) error {
|
||||||
|
|
||||||
|
DCIDbs := make([]byte, 4)
|
||||||
|
|
||||||
|
binary.LittleEndian.PutUint32(DCIDbs, DCID)
|
||||||
|
|
||||||
|
err := tx.Bucket(JID_TO_DCID).Put([]byte(JID), DCIDbs)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Bucket(DCID_TO_JID).Put(DCIDbs, []byte(JID))
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) Put(key []byte, value []byte) error {
|
||||||
|
err := d.db.Update(func(tx *bbolt.Tx) error {
|
||||||
|
err := tx.Bucket(KEY_VALUE).Put(key, value)
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) Get(key []byte) []byte {
|
||||||
|
var value []byte
|
||||||
|
|
||||||
|
d.db.View(func(tx *bbolt.Tx) error {
|
||||||
|
value = tx.Bucket(KEY_VALUE).Get(key)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
"github.com/hugot/go-deltachat/deltachat"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WhappHandler struct {
|
||||||
|
dcContext *deltachat.Context
|
||||||
|
db *Database
|
||||||
|
dcUserID uint32
|
||||||
|
wac *whatsapp.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find or create a deltachat verified group chat for a whatsapp JID and return it's ID.
|
||||||
|
func (h *WhappHandler) getOrCreateDCIDForJID(JID string, isGroup bool) (uint32, error) {
|
||||||
|
if DCID, _ := h.db.GetDCIDForWhappJID(JID); DCID != nil {
|
||||||
|
return *DCID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
chatName := JID
|
||||||
|
if isGroup {
|
||||||
|
chat, ok := h.wac.Store.Chats[JID]
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
chatName = chat.Name
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
contact, ok := h.wac.Store.Contacts[JID]
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
chatName = contact.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DCID := h.dcContext.CreateGroupChat(true, chatName)
|
||||||
|
|
||||||
|
err := h.db.StoreDCIDForJID(JID, DCID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return DCID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.dcContext.AddContactToChat(DCID, h.dcUserID)
|
||||||
|
|
||||||
|
return DCID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *WhappHandler) HandleError(err error) {
|
||||||
|
log.Println("Whatsapp Error: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *WhappHandler) HandleTextMessage(m whatsapp.TextMessage) {
|
||||||
|
JID := m.Info.RemoteJid
|
||||||
|
|
||||||
|
DCID, err := h.getOrCreateDCIDForJID(JID, m.Info.RemoteJid != m.Info.SenderJid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
|
||||||
|
chatID := h.dcContext.GetChatIDByContactID(h.dcUserID)
|
||||||
|
h.dcContext.SendTextMessage(
|
||||||
|
chatID,
|
||||||
|
err.Error(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
senderName := m.Info.SenderJid
|
||||||
|
contact, ok := h.wac.Store.Contacts[m.Info.SenderJid]
|
||||||
|
if ok {
|
||||||
|
senderName = contact.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
h.dcContext.SendTextMessage(
|
||||||
|
DCID,
|
||||||
|
fmt.Sprintf("%s:\n%s", senderName, m.Text),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"mime"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
"github.com/hugot/go-deltachat/deltachat"
|
||||||
|
"github.com/skip2/go-qrcode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateAndLoginWhappConnection(
|
||||||
|
storageDir string,
|
||||||
|
dcContext *deltachat.Context,
|
||||||
|
dcUserID uint32,
|
||||||
|
) (*whatsapp.Conn, error) {
|
||||||
|
wac, err := whatsapp.NewConn(20 * time.Second)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return wac, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var session whatsapp.Session
|
||||||
|
|
||||||
|
sessionFile := storageDir + "/whapp-session.json"
|
||||||
|
if _, err := os.Stat(sessionFile); os.IsNotExist(err) {
|
||||||
|
session, err = WhappQrLogin(storageDir, wac, dcContext, dcUserID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return wac, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
session = whatsapp.Session{}
|
||||||
|
|
||||||
|
sessionJson, err := ioutil.ReadFile(sessionFile)
|
||||||
|
|
||||||
|
err = json.Unmarshal(sessionJson, &session)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return wac, err
|
||||||
|
}
|
||||||
|
|
||||||
|
session, err = wac.RestoreWithSession(session)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return wac, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = StoreWhappSession(session, storageDir)
|
||||||
|
|
||||||
|
return wac, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func WhappQrLogin(
|
||||||
|
storageDir string,
|
||||||
|
wac *whatsapp.Conn,
|
||||||
|
dcContext *deltachat.Context,
|
||||||
|
dcUserID uint32,
|
||||||
|
) (whatsapp.Session, error) {
|
||||||
|
qrChan := make(chan string)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
qrCode := <-qrChan
|
||||||
|
|
||||||
|
tmpFile, err := ioutil.TempFile(storageDir+"/tmp", "qr")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = qrcode.WriteFile(qrCode, qrcode.Medium, 256, tmpFile.Name())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
message := dcContext.NewMessage(deltachat.DC_MSG_IMAGE)
|
||||||
|
|
||||||
|
log.Println("MIME: " + mime.TypeByExtension("png"))
|
||||||
|
|
||||||
|
message.SetFile(tmpFile.Name(), "image/png")
|
||||||
|
|
||||||
|
message.SetText("Scan this QR code from whatsapp")
|
||||||
|
|
||||||
|
dcContext.SendMessage(
|
||||||
|
dcContext.GetChatIDByContactID(dcUserID),
|
||||||
|
message,
|
||||||
|
)
|
||||||
|
}()
|
||||||
|
|
||||||
|
session, err := wac.Login(qrChan)
|
||||||
|
|
||||||
|
return session, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func StoreWhappSession(session whatsapp.Session, storageDir string) error {
|
||||||
|
sessionJson, err := json.Marshal(session)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(storageDir+"/whapp-session.json", sessionJson, 0600)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
Loading…
Reference in New Issue