Compare commits

...

5 Commits

Author SHA1 Message Date
normen
04960123da fix freeze when downloading messages 2020-11-21 21:13:38 +01:00
normen
ab03aeb7a0 error output and README improvements 2020-11-20 14:40:01 +01:00
normen
f6860b56b8 secure connection handling with mutex 2020-11-20 14:31:17 +01:00
normen
6bea425367 make message database thread safe 2020-11-20 14:10:09 +01:00
normen
1b3379b613 learning GO - using its sugar 2020-11-20 13:24:15 +01:00
4 changed files with 74 additions and 49 deletions

View File

@@ -38,11 +38,11 @@ Always fresh, always up to date.
- Download a release
- Put the binary in your PATH (optional)
- Run with `whatscli` (or double-click)
- Scan the QR code with WhatsApp on your phone (maybe resize shell)
- Scan the QR code with WhatsApp on your phone (resize shell or change font size to see whole code)
### Package Managers
Some unofficial ways to install via package managers are supported but the installed version might be out of date.
Some ways to install via package managers are supported but the installed version might be out of date.
#### MacOS (homebrew)

View File

@@ -19,7 +19,7 @@ type waMsg struct {
Text string
}
var VERSION string = "v0.6.8"
var VERSION string = "v0.6.10"
var sendChannel chan waMsg
var textChannel chan whatsapp.TextMessage
@@ -552,7 +552,7 @@ type textHandler struct{}
// HandleError implements the error handler interface for go-whatsapp
func (t textHandler) HandleError(err error) {
// TODO : handle go routine here
PrintText("[red]go-whatsapp reported an error:[-]")
PrintError(err)
return
}

View File

@@ -3,15 +3,18 @@ package messages
import (
"encoding/gob"
"fmt"
"github.com/rivo/tview"
"os"
"sync"
"time"
"github.com/rivo/tview"
"github.com/Rhymen/go-whatsapp"
"github.com/normen/whatscli/qrcode"
)
var textView *tview.TextView
var connMutex sync.Mutex
// TODO: remove this circular dependeny in favor of a better way
func SetTextView(tv *tview.TextView) {
@@ -20,6 +23,8 @@ func SetTextView(tv *tview.TextView) {
// gets an existing connection or creates one
func GetConnection() *whatsapp.Conn {
connMutex.Lock()
defer connMutex.Unlock()
var wac *whatsapp.Conn
if connection == nil {
wacc, err := whatsapp.NewConn(5 * time.Second)
@@ -44,8 +49,10 @@ func Login() error {
// LoginWithConnection logs in the user using a provided connection. It ries to see if a session already exists. If not, tries to create a
// new one using qr scanned on the terminal.
func LoginWithConnection(wac *whatsapp.Conn) error {
if connection != nil && connection.GetConnected() {
connection.Disconnect()
connMutex.Lock()
defer connMutex.Unlock()
if wac != nil && wac.GetConnected() {
wac.Disconnect()
}
//load saved session
session, err := readSession()
@@ -80,6 +87,8 @@ func LoginWithConnection(wac *whatsapp.Conn) error {
// Logout logs out the user.
func Logout() error {
connMutex.Lock()
defer connMutex.Unlock()
return removeSession()
}

View File

@@ -6,6 +6,7 @@ import (
"os"
"sort"
"strings"
"sync"
"time"
"github.com/Rhymen/go-whatsapp"
@@ -21,41 +22,46 @@ type MessageDatabase struct {
messagesById map[string]*whatsapp.TextMessage // text messages stored by message ID
latestMessage map[string]uint64 // last message from RemoteJid
otherMessages map[string]*interface{} // other non-text messages, stored by ID
mutex sync.Mutex
}
// initialize the database
func (db *MessageDatabase) Init() {
//var this = *db
(*db).textMessages = make(map[string][]*whatsapp.TextMessage)
(*db).messagesById = make(map[string]*whatsapp.TextMessage)
(*db).otherMessages = make(map[string]*interface{})
(*db).latestMessage = make(map[string]uint64)
db.textMessages = make(map[string][]*whatsapp.TextMessage)
db.messagesById = make(map[string]*whatsapp.TextMessage)
db.otherMessages = make(map[string]*interface{})
db.latestMessage = make(map[string]uint64)
}
// add a text message to the database, stored by RemoteJid
func (db *MessageDatabase) AddTextMessage(msg *whatsapp.TextMessage) bool {
db.mutex.Lock()
defer db.mutex.Unlock()
//var this = *db
var didNew = false
var wid = msg.Info.RemoteJid
if (*db).textMessages[wid] == nil {
if db.textMessages[wid] == nil {
var newArr = []*whatsapp.TextMessage{}
(*db).textMessages[wid] = newArr
(*db).latestMessage[wid] = msg.Info.Timestamp
db.textMessages[wid] = newArr
db.latestMessage[wid] = msg.Info.Timestamp
didNew = true
} else if (*db).latestMessage[wid] < msg.Info.Timestamp {
(*db).latestMessage[wid] = msg.Info.Timestamp
} else if db.latestMessage[wid] < msg.Info.Timestamp {
db.latestMessage[wid] = msg.Info.Timestamp
didNew = true
}
(*db).textMessages[wid] = append((*db).textMessages[wid], msg)
(*db).messagesById[msg.Info.Id] = msg
sort.Slice((*db).textMessages[wid], func(i, j int) bool {
return (*db).textMessages[wid][i].Info.Timestamp < (*db).textMessages[wid][j].Info.Timestamp
db.textMessages[wid] = append(db.textMessages[wid], msg)
db.messagesById[msg.Info.Id] = msg
sort.Slice(db.textMessages[wid], func(i, j int) bool {
return db.textMessages[wid][i].Info.Timestamp < db.textMessages[wid][j].Info.Timestamp
})
return didNew
}
// add audio/video/image/doc message, stored by message id
func (db *MessageDatabase) AddOtherMessage(msg *interface{}) {
db.mutex.Lock()
defer db.mutex.Unlock()
var id = ""
switch v := (*msg).(type) {
default:
@@ -69,31 +75,35 @@ func (db *MessageDatabase) AddOtherMessage(msg *interface{}) {
id = v.Info.Id
}
if id != "" {
(*db).otherMessages[id] = msg
db.otherMessages[id] = msg
}
}
// get an array of all chat ids
func (db *MessageDatabase) GetContactIds() []string {
db.mutex.Lock()
defer db.mutex.Unlock()
//var this = *db
keys := make([]string, len((*db).textMessages))
keys := make([]string, len(db.textMessages))
i := 0
for k := range (*db).textMessages {
for k := range db.textMessages {
keys[i] = k
i++
}
sort.Slice(keys, func(i, j int) bool {
return (*db).latestMessage[keys[i]] > (*db).latestMessage[keys[j]]
return db.latestMessage[keys[i]] > db.latestMessage[keys[j]]
})
return keys
}
func (db *MessageDatabase) GetMessageInfo(id string) string {
if _, ok := (*db).otherMessages[id]; ok {
db.mutex.Lock()
defer db.mutex.Unlock()
if _, ok := db.otherMessages[id]; ok {
return "[yellow]OtherMessage[-]"
}
out := ""
if msg, ok := (*db).messagesById[id]; ok {
if msg, ok := db.messagesById[id]; ok {
out += "[yellow]ID: " + msg.Info.Id + "[-]\n"
out += "[yellow]PushName: " + msg.Info.PushName + "[-]\n"
out += "[yellow]RemoteJid: " + msg.Info.RemoteJid + "[-]\n"
@@ -106,10 +116,12 @@ func (db *MessageDatabase) GetMessageInfo(id string) string {
// get a string containing all messages for a chat by chat id
func (db *MessageDatabase) GetMessagesString(wid string) (string, []string) {
db.mutex.Lock()
defer db.mutex.Unlock()
//var this = *db
var out = ""
var arr = []string{}
for _, element := range (*db).textMessages[wid] {
for _, element := range db.textMessages[wid] {
out += GetTextMessageString(element)
out += "\n"
arr = append(arr, element.Info.Id)
@@ -117,29 +129,11 @@ func (db *MessageDatabase) GetMessagesString(wid string) (string, []string) {
return out, arr
}
// create a formatted string with regions based on message ID from a text message
func GetTextMessageString(msg *whatsapp.TextMessage) string {
out := ""
text := tview.Escape((*msg).Text)
tim := time.Unix(int64((*msg).Info.Timestamp), 0)
out += "[\""
out += (*msg).Info.Id
out += "\"]"
if (*msg).Info.FromMe { //msg from me
out += "[-::d](" + tim.Format("02-01-06 15:04:05") + ") [blue::b]Me: [-::-]" + text
} else if strings.Contains((*msg).Info.RemoteJid, GROUPSUFFIX) { // group msg
userId := (*msg).Info.SenderJid
out += "[-::d](" + tim.Format("02-01-06 15:04:05") + ") [green::b]" + GetIdShort(userId) + ": [-::-]" + text
} else { // message from others
out += "[-::d](" + tim.Format("02-01-06 15:04:05") + ") [green::b]" + GetIdShort((*msg).Info.RemoteJid) + ": [-::-]" + text
}
out += "[\"\"]"
return out
}
// load data for message specified by message id TODO: support types
func (db *MessageDatabase) LoadMessageData(wid string) ([]byte, error) {
if msg, ok := (*db).otherMessages[wid]; ok {
db.mutex.Lock()
defer db.mutex.Unlock()
if msg, ok := db.otherMessages[wid]; ok {
switch v := (*msg).(type) {
default:
case whatsapp.ImageMessage:
@@ -157,7 +151,9 @@ func (db *MessageDatabase) LoadMessageData(wid string) ([]byte, error) {
// attempts to download a messages attachments, returns path or error message
func (db *MessageDatabase) DownloadMessage(wid string, open bool) (string, error) {
if msg, ok := (*db).otherMessages[wid]; ok {
db.mutex.Lock()
defer db.mutex.Unlock()
if msg, ok := db.otherMessages[wid]; ok {
var fileName string = GetHomeDir() + "Downloads" + string(os.PathSeparator)
switch v := (*msg).(type) {
default:
@@ -210,6 +206,26 @@ func (db *MessageDatabase) DownloadMessage(wid string, open bool) (string, error
return "", errors.New("No attachments found")
}
// create a formatted string with regions based on message ID from a text message
func GetTextMessageString(msg *whatsapp.TextMessage) string {
out := ""
text := tview.Escape(msg.Text)
tim := time.Unix(int64(msg.Info.Timestamp), 0)
out += "[\""
out += msg.Info.Id
out += "\"]"
if msg.Info.FromMe { //msg from me
out += "[-::d](" + tim.Format("02-01-06 15:04:05") + ") [blue::b]Me: [-::-]" + text
} else if strings.Contains(msg.Info.RemoteJid, GROUPSUFFIX) { // group msg
userId := msg.Info.SenderJid
out += "[-::d](" + tim.Format("02-01-06 15:04:05") + ") [green::b]" + GetIdShort(userId) + ": [-::-]" + text
} else { // message from others
out += "[-::d](" + tim.Format("02-01-06 15:04:05") + ") [green::b]" + GetIdShort(msg.Info.RemoteJid) + ": [-::-]" + text
}
out += "[\"\"]"
return out
}
// helper to save an attachment and open it if specified
func saveAttachment(data []byte, path string, openIt bool) (string, error) {
err := ioutil.WriteFile(path, data, 0644)