Add puppet and portal stuff and fix config stuff
This commit is contained in:
parent
fd3c1fb77c
commit
9c48eeb534
12 changed files with 474 additions and 30 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,2 +1,5 @@
|
|||
.idea
|
||||
*.session
|
||||
|
||||
*.yaml
|
||||
!example-config.yaml
|
||||
|
|
|
@ -22,24 +22,26 @@ import (
|
|||
)
|
||||
|
||||
type BridgeConfig struct {
|
||||
RawUsernameTemplate string `yaml:"username_template"`
|
||||
RawDisplaynameTemplate string `yaml:"displayname_template"`
|
||||
UsernameTemplate *template.Template `yaml:"-"`
|
||||
DisplaynameTemplate *template.Template `yaml:"-"`
|
||||
UsernameTemplate string `yaml:"username_template"`
|
||||
DisplaynameTemplate string `yaml:"displayname_template"`
|
||||
usernameTemplate *template.Template `yaml:"-"`
|
||||
displaynameTemplate *template.Template `yaml:"-"`
|
||||
}
|
||||
|
||||
func (bc BridgeConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
err := unmarshal(bc)
|
||||
type umBridgeConfig BridgeConfig
|
||||
|
||||
func (bc *BridgeConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
err := unmarshal((*umBridgeConfig)(bc))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bc.UsernameTemplate, err = template.New("username").Parse(bc.RawUsernameTemplate)
|
||||
bc.usernameTemplate, err = template.New("username").Parse(bc.UsernameTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bc.DisplaynameTemplate, err = template.New("displayname").Parse(bc.RawDisplaynameTemplate)
|
||||
bc.displaynameTemplate, err = template.New("displayname").Parse(bc.DisplaynameTemplate)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -54,7 +56,7 @@ type UsernameTemplateArgs struct {
|
|||
|
||||
func (bc BridgeConfig) FormatDisplayname(displayname string) string {
|
||||
var buf bytes.Buffer
|
||||
bc.DisplaynameTemplate.Execute(&buf, DisplaynameTemplateArgs{
|
||||
bc.displaynameTemplate.Execute(&buf, DisplaynameTemplateArgs{
|
||||
Displayname: displayname,
|
||||
})
|
||||
return buf.String()
|
||||
|
@ -62,7 +64,7 @@ func (bc BridgeConfig) FormatDisplayname(displayname string) string {
|
|||
|
||||
func (bc BridgeConfig) FormatUsername(receiver, userID string) string {
|
||||
var buf bytes.Buffer
|
||||
bc.UsernameTemplate.Execute(&buf, UsernameTemplateArgs{
|
||||
bc.usernameTemplate.Execute(&buf, UsernameTemplateArgs{
|
||||
Receiver: receiver,
|
||||
UserID: userID,
|
||||
})
|
||||
|
@ -70,7 +72,7 @@ func (bc BridgeConfig) FormatUsername(receiver, userID string) string {
|
|||
}
|
||||
|
||||
func (bc BridgeConfig) MarshalYAML() (interface{}, error) {
|
||||
bc.RawDisplaynameTemplate = bc.FormatDisplayname("{{.Displayname}}")
|
||||
bc.RawUsernameTemplate = bc.FormatUsername("{{.Receiver}}", "{{.UserID}}")
|
||||
bc.DisplaynameTemplate = bc.FormatDisplayname("{{.Displayname}}")
|
||||
bc.UsernameTemplate = bc.FormatUsername("{{.Receiver}}", "{{.UserID}}")
|
||||
return bc, nil
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package config
|
|||
import (
|
||||
"maunium.net/go/mautrix-appservice"
|
||||
"regexp"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (config *Config) NewRegistration() (*appservice.Registration, error) {
|
||||
|
@ -53,7 +54,9 @@ func (config *Config) copyToRegistration(registration *appservice.Registration)
|
|||
registration.RateLimited = false
|
||||
registration.SenderLocalpart = config.AppService.Bot.Username
|
||||
|
||||
userIDRegex, err := regexp.Compile(config.Bridge.FormatUsername("[0-9]+", "[0-9]+"))
|
||||
userIDRegex, err := regexp.Compile(fmt.Sprintf("@%s:%s",
|
||||
config.Bridge.FormatUsername("[0-9]+", "[0-9]+"),
|
||||
config.Homeserver.Domain))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -26,7 +26,9 @@ type Database struct {
|
|||
*sql.DB
|
||||
log *log.Sublogger
|
||||
|
||||
User *UserQuery
|
||||
User *UserQuery
|
||||
Portal *PortalQuery
|
||||
Puppet *PuppetQuery
|
||||
}
|
||||
|
||||
func New(file string) (*Database, error) {
|
||||
|
@ -43,6 +45,14 @@ func New(file string) (*Database, error) {
|
|||
db: db,
|
||||
log: log.CreateSublogger("Database/User", log.LevelDebug),
|
||||
}
|
||||
db.Portal = &PortalQuery{
|
||||
db: db,
|
||||
log: log.CreateSublogger("Database/Portal", log.LevelDebug),
|
||||
}
|
||||
db.Puppet = &PuppetQuery{
|
||||
db: db,
|
||||
log: log.CreateSublogger("Database/Puppet", log.LevelDebug),
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -44,8 +44,8 @@ func (pq *PortalQuery) New() *Portal {
|
|||
}
|
||||
}
|
||||
|
||||
func (pq *PortalQuery) GetAll() (portals []*Portal) {
|
||||
rows, err := pq.db.Query("SELECT * FROM portal")
|
||||
func (pq *PortalQuery) GetAll(owner string) (portals []*Portal) {
|
||||
rows, err := pq.db.Query("SELECT * FROM portal WHERE owner=?", owner)
|
||||
if err != nil || rows == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
98
database/puppet.go
Normal file
98
database/puppet.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||
// Copyright (C) 2018 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
log "maunium.net/go/maulogger"
|
||||
)
|
||||
|
||||
type PuppetQuery struct {
|
||||
db *Database
|
||||
log *log.Sublogger
|
||||
}
|
||||
|
||||
func (pq *PuppetQuery) CreateTable() error {
|
||||
_, err := pq.db.Exec(`CREATE TABLE IF NOT EXISTS puppet (
|
||||
jid VARCHAR(255),
|
||||
receiver VARCHAR(255),
|
||||
|
||||
displayname VARCHAR(255),
|
||||
avatar VARCHAR(255),
|
||||
|
||||
PRIMARY KEY(jid, receiver)
|
||||
)`)
|
||||
return err
|
||||
}
|
||||
|
||||
func (pq *PuppetQuery) New() *Puppet {
|
||||
return &Puppet{
|
||||
db: pq.db,
|
||||
log: pq.log,
|
||||
}
|
||||
}
|
||||
|
||||
func (pq *PuppetQuery) GetAll() (puppets []*Puppet) {
|
||||
rows, err := pq.db.Query("SELECT * FROM puppet")
|
||||
if err != nil || rows == nil {
|
||||
return nil
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
puppets = append(puppets, pq.New().Scan(rows))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (pq *PuppetQuery) Get(jid, receiver string) *Puppet {
|
||||
row := pq.db.QueryRow("SELECT * FROM user WHERE jid=? AND receiver=?", jid, receiver)
|
||||
if row == nil {
|
||||
return nil
|
||||
}
|
||||
return pq.New().Scan(row)
|
||||
}
|
||||
|
||||
type Puppet struct {
|
||||
db *Database
|
||||
log *log.Sublogger
|
||||
|
||||
JID string
|
||||
Receiver string
|
||||
|
||||
Displayname string
|
||||
Avatar string
|
||||
}
|
||||
|
||||
func (puppet *Puppet) Scan(row Scannable) *Puppet {
|
||||
err := row.Scan(&puppet.JID, &puppet.Receiver, &puppet.Displayname, &puppet.Avatar)
|
||||
if err != nil {
|
||||
puppet.log.Fatalln("Database scan failed:", err)
|
||||
}
|
||||
return puppet
|
||||
}
|
||||
|
||||
func (puppet *Puppet) Insert() error {
|
||||
_, err := puppet.db.Exec("INSERT INTO puppet VALUES (?, ?, ?, ?)",
|
||||
puppet.JID, puppet.Receiver, puppet.Displayname, puppet.Avatar)
|
||||
return err
|
||||
}
|
||||
|
||||
func (puppet *Puppet) Update() error {
|
||||
_, err := puppet.db.Exec("UPDATE puppet SET displayname=?, avatar=? WHERE jid=? AND receiver=?",
|
||||
puppet.Displayname, puppet.Avatar,
|
||||
puppet.JID, puppet.Receiver)
|
||||
return err
|
||||
}
|
|
@ -28,7 +28,9 @@ type UserQuery struct {
|
|||
|
||||
func (uq *UserQuery) CreateTable() error {
|
||||
_, err := uq.db.Exec(`CREATE TABLE IF NOT EXISTS user (
|
||||
mxid VARCHAR(255) PRIMARY KEY,
|
||||
mxid VARCHAR(255) PRIMARY KEY,
|
||||
|
||||
management_room VARCHAR(255),
|
||||
|
||||
client_id VARCHAR(255),
|
||||
client_token VARCHAR(255),
|
||||
|
@ -71,29 +73,43 @@ type User struct {
|
|||
db *Database
|
||||
log *log.Sublogger
|
||||
|
||||
UserID string
|
||||
|
||||
session whatsapp.Session
|
||||
UserID string
|
||||
ManagementRoom string
|
||||
Session *whatsapp.Session
|
||||
}
|
||||
|
||||
func (user *User) Scan(row Scannable) *User {
|
||||
err := row.Scan(&user.UserID, &user.session.ClientId, &user.session.ClientToken, &user.session.ServerToken,
|
||||
&user.session.EncKey, &user.session.MacKey, &user.session.Wid)
|
||||
sess := whatsapp.Session{}
|
||||
err := row.Scan(&user.UserID, &user.ManagementRoom, &sess.ClientId, &sess.ClientToken, &sess.ServerToken,
|
||||
&sess.EncKey, &sess.MacKey, &sess.Wid)
|
||||
if err != nil {
|
||||
user.log.Fatalln("Database scan failed:", err)
|
||||
}
|
||||
if len(sess.ClientId) > 0 {
|
||||
user.Session = &sess
|
||||
} else {
|
||||
user.Session = nil
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
func (user *User) Insert() error {
|
||||
_, err := user.db.Exec("INSERT INTO user VALUES (?, ?, ?, ?, ?, ?, ?)", user.UserID, user.session.ClientId,
|
||||
user.session.ClientToken, user.session.ServerToken, user.session.EncKey, user.session.MacKey, user.session.Wid)
|
||||
var sess whatsapp.Session
|
||||
if user.Session != nil {
|
||||
sess = *user.Session
|
||||
}
|
||||
_, err := user.db.Exec("INSERT INTO user VALUES (?, ?, ?, ?, ?, ?, ?, ?)", user.UserID, user.ManagementRoom,
|
||||
sess.ClientId, sess.ClientToken, sess.ServerToken, sess.EncKey, sess.MacKey, sess.Wid)
|
||||
return err
|
||||
}
|
||||
|
||||
func (user *User) Update() error {
|
||||
_, err := user.db.Exec("UPDATE user SET client_id=?, client_token=?, server_token=?, enc_key=?, mac_key=?, wid=? WHERE mxid=?",
|
||||
user.session.ClientId, user.session.ClientToken, user.session.ServerToken, user.session.EncKey, user.session.MacKey,
|
||||
user.session.Wid, user.UserID)
|
||||
var sess whatsapp.Session
|
||||
if user.Session != nil {
|
||||
sess = *user.Session
|
||||
}
|
||||
_, err := user.db.Exec("UPDATE user SET management_room=?, client_id=?, client_token=?, server_token=?, enc_key=?, mac_key=?, wid=? WHERE mxid=?",
|
||||
user.ManagementRoom,
|
||||
sess.ClientId, sess.ClientToken, sess.ServerToken, sess.EncKey, sess.MacKey, sess.Wid, user.UserID)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ logging:
|
|||
# The directory for log files. Will be created if not found.
|
||||
directory: ./logs
|
||||
# Available variables: .date for the file date and .index for different log files on the same day.
|
||||
file_name_format: {{.date}}-{{.index}.log
|
||||
file_name_format: "{{.date}}-{{.index}.log"
|
||||
# Date format for file names in the Go time format: https://golang.org/pkg/time/#pkg-constants
|
||||
file_date_format: 2006-01-02
|
||||
# Log file permissions.
|
||||
|
|
6
main.go
6
main.go
|
@ -67,6 +67,8 @@ type Bridge struct {
|
|||
Log *log.Logger
|
||||
|
||||
MatrixListener *MatrixListener
|
||||
|
||||
users map[string]*User
|
||||
}
|
||||
|
||||
func NewBridge() *Bridge {
|
||||
|
@ -133,7 +135,9 @@ func (bridge *Bridge) Main() {
|
|||
}
|
||||
|
||||
func main() {
|
||||
flag.SetHelpTitles("mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.", "[-h] [-c <path>] [-r <path>] [-g]")
|
||||
flag.SetHelpTitles(
|
||||
"mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.",
|
||||
"mautrix-whatsapp [-h] [-c <path>] [-r <path>] [-g]")
|
||||
err := flag.Parse()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
|
|
63
matrix.go
63
matrix.go
|
@ -18,10 +18,13 @@ package main
|
|||
|
||||
import (
|
||||
log "maunium.net/go/maulogger"
|
||||
"maunium.net/go/mautrix-appservice"
|
||||
"maunium.net/go/gomatrix"
|
||||
)
|
||||
|
||||
type MatrixListener struct {
|
||||
bridge *Bridge
|
||||
as *appservice.AppService
|
||||
log *log.Sublogger
|
||||
stop chan struct{}
|
||||
}
|
||||
|
@ -29,8 +32,9 @@ type MatrixListener struct {
|
|||
func NewMatrixListener(bridge *Bridge) *MatrixListener {
|
||||
return &MatrixListener{
|
||||
bridge: bridge,
|
||||
as: bridge.AppService,
|
||||
stop: make(chan struct{}, 1),
|
||||
log: bridge.Log.CreateSublogger("Matrix", log.LevelDebug),
|
||||
log: bridge.Log.CreateSublogger("Matrix", log.LevelDebug),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,12 +43,69 @@ func (ml *MatrixListener) Start() {
|
|||
select {
|
||||
case evt := <-ml.bridge.AppService.Events:
|
||||
log.Debugln("Received Matrix event:", evt)
|
||||
switch evt.Type {
|
||||
case gomatrix.StateMember:
|
||||
ml.HandleMembership(evt)
|
||||
case gomatrix.EventMessage:
|
||||
ml.HandleMessage(evt)
|
||||
}
|
||||
case <-ml.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ml *MatrixListener) HandleBotInvite(evt *gomatrix.Event) {
|
||||
cli := ml.as.BotClient()
|
||||
|
||||
resp, err := cli.JoinRoom(evt.RoomID, "", nil)
|
||||
if err != nil {
|
||||
ml.log.Debugln("Failed to join room", evt.RoomID, "with invite from", evt.Sender)
|
||||
return
|
||||
}
|
||||
|
||||
members, err := cli.JoinedMembers(resp.RoomID)
|
||||
if err != nil {
|
||||
ml.log.Debugln("Failed to get members in room", resp.RoomID, "after accepting invite from", evt.Sender)
|
||||
cli.LeaveRoom(resp.RoomID)
|
||||
return
|
||||
}
|
||||
|
||||
if len(members.Joined) < 2 {
|
||||
ml.log.Debugln("Leaving empty room", resp.RoomID, "after accepting invite from", evt.Sender)
|
||||
cli.LeaveRoom(resp.RoomID)
|
||||
return
|
||||
}
|
||||
for mxid, _ := range members.Joined {
|
||||
if mxid == cli.UserID || mxid == evt.Sender {
|
||||
continue
|
||||
} else if true { // TODO check if mxid is WhatsApp puppet
|
||||
|
||||
continue
|
||||
}
|
||||
ml.log.Debugln("Leaving multi-user room", resp.RoomID, "after accepting invite from", evt.Sender)
|
||||
cli.SendNotice(resp.RoomID, "This bridge is user-specific, please don't invite me into rooms with other users.")
|
||||
cli.LeaveRoom(resp.RoomID)
|
||||
return
|
||||
}
|
||||
|
||||
user := ml.bridge.GetUser(evt.Sender)
|
||||
user.ManagementRoom = resp.RoomID
|
||||
user.Update()
|
||||
cli.SendNotice(user.ManagementRoom, "This room has been registered as your bridge management/status room.")
|
||||
ml.log.Debugln(resp.RoomID, "registered as a management room with", evt.Sender)
|
||||
}
|
||||
|
||||
func (ml *MatrixListener) HandleMembership(evt *gomatrix.Event) {
|
||||
if evt.Content.Membership == "invite" && evt.GetStateKey() == ml.as.BotMXID() {
|
||||
ml.HandleBotInvite(evt)
|
||||
}
|
||||
}
|
||||
|
||||
func (ml *MatrixListener) HandleMessage(evt *gomatrix.Event) {
|
||||
|
||||
}
|
||||
|
||||
func (ml *MatrixListener) Stop() {
|
||||
ml.stop <- struct{}{}
|
||||
}
|
||||
|
|
89
portal.go
Normal file
89
portal.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||
// Copyright (C) 2018 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"maunium.net/go/mautrix-whatsapp/database"
|
||||
log "maunium.net/go/maulogger"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (user *User) GetPortalByMXID(mxid string) *Portal {
|
||||
portal, ok := user.portalsByMXID[mxid]
|
||||
if !ok {
|
||||
dbPortal := user.bridge.DB.Portal.GetByMXID(mxid)
|
||||
if dbPortal == nil || dbPortal.Owner != user.UserID {
|
||||
return nil
|
||||
}
|
||||
portal = user.NewPortal(dbPortal)
|
||||
user.portalsByJID[portal.JID] = portal
|
||||
if len(portal.MXID) > 0 {
|
||||
user.portalsByMXID[portal.MXID] = portal
|
||||
}
|
||||
}
|
||||
return portal
|
||||
}
|
||||
|
||||
func (user *User) GetPortalByJID(jid string) *Portal {
|
||||
portal, ok := user.portalsByJID[jid]
|
||||
if !ok {
|
||||
dbPortal := user.bridge.DB.Portal.GetByJID(user.UserID, jid)
|
||||
if dbPortal == nil {
|
||||
return nil
|
||||
}
|
||||
portal = user.NewPortal(dbPortal)
|
||||
user.portalsByJID[portal.JID] = portal
|
||||
if len(portal.MXID) > 0 {
|
||||
user.portalsByMXID[portal.MXID] = portal
|
||||
}
|
||||
}
|
||||
return portal
|
||||
}
|
||||
|
||||
func (user *User) GetAllPortals() []*Portal {
|
||||
dbPortals := user.bridge.DB.Portal.GetAll(user.UserID)
|
||||
output := make([]*Portal, len(dbPortals))
|
||||
for index, dbPortal := range dbPortals {
|
||||
portal, ok := user.portalsByJID[dbPortal.JID]
|
||||
if !ok {
|
||||
portal = user.NewPortal(dbPortal)
|
||||
user.portalsByJID[dbPortal.JID] = portal
|
||||
if len(dbPortal.MXID) > 0 {
|
||||
user.portalsByMXID[dbPortal.MXID] = portal
|
||||
}
|
||||
}
|
||||
output[index] = portal
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
func (user *User) NewPortal(dbPortal *database.Portal) *Portal {
|
||||
return &Portal{
|
||||
Portal: dbPortal,
|
||||
user: user,
|
||||
bridge: user.bridge,
|
||||
log: user.bridge.Log.CreateSublogger(fmt.Sprintf("Portal/%s/%s", user.UserID, dbPortal.JID), log.LevelDebug),
|
||||
}
|
||||
}
|
||||
|
||||
type Portal struct {
|
||||
*database.Portal
|
||||
|
||||
user *User
|
||||
bridge *Bridge
|
||||
log *log.Sublogger
|
||||
}
|
158
user.go
158
user.go
|
@ -15,3 +15,161 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"maunium.net/go/mautrix-whatsapp/database"
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
"time"
|
||||
"fmt"
|
||||
"os"
|
||||
"github.com/skip2/go-qrcode"
|
||||
log "maunium.net/go/maulogger"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
*database.User
|
||||
Conn *whatsapp.Conn
|
||||
|
||||
bridge *Bridge
|
||||
log *log.Sublogger
|
||||
|
||||
portalsByMXID map[string]*Portal
|
||||
portalsByJID map[string]*Portal
|
||||
puppets map[string]*Portal
|
||||
}
|
||||
|
||||
func (bridge *Bridge) GetUser(userID string) *User {
|
||||
user, ok := bridge.users[userID]
|
||||
if !ok {
|
||||
dbUser := bridge.DB.User.Get(userID)
|
||||
if dbUser == nil {
|
||||
dbUser = bridge.DB.User.New()
|
||||
dbUser.Insert()
|
||||
}
|
||||
user = bridge.NewUser(dbUser)
|
||||
bridge.users[user.UserID] = user
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
func (bridge *Bridge) GetAllUsers() []*User {
|
||||
dbUsers := bridge.DB.User.GetAll()
|
||||
output := make([]*User, len(dbUsers))
|
||||
for index, dbUser := range dbUsers {
|
||||
user, ok := bridge.users[dbUser.UserID]
|
||||
if !ok {
|
||||
user = bridge.NewUser(dbUser)
|
||||
bridge.users[user.UserID] = user
|
||||
}
|
||||
output[index] = user
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
func (bridge *Bridge) InitWhatsApp() {
|
||||
users := bridge.GetAllUsers()
|
||||
for _, user := range users {
|
||||
user.Connect()
|
||||
}
|
||||
}
|
||||
|
||||
func (bridge *Bridge) NewUser(dbUser *database.User) *User {
|
||||
return &User{
|
||||
User: dbUser,
|
||||
bridge: bridge,
|
||||
log: bridge.Log.CreateSublogger(fmt.Sprintf("User/%s", dbUser.UserID), log.LevelDebug),
|
||||
}
|
||||
}
|
||||
|
||||
func (user *User) Connect() {
|
||||
var err error
|
||||
user.Conn, err = whatsapp.NewConn(20 * time.Second)
|
||||
if err != nil {
|
||||
user.log.Errorln("Failed to connect to WhatsApp:", err)
|
||||
return
|
||||
}
|
||||
user.Conn.AddHandler(user)
|
||||
user.RestoreSession()
|
||||
}
|
||||
|
||||
func (user *User) RestoreSession() {
|
||||
if user.Session != nil {
|
||||
sess, err := user.Conn.RestoreSession(*user.Session)
|
||||
if err != nil {
|
||||
user.log.Errorln("Failed to restore session:", err)
|
||||
user.Session = nil
|
||||
return
|
||||
}
|
||||
user.Session = &sess
|
||||
user.log.Debugln("Session restored")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (user *User) Login(roomID string) {
|
||||
bot := user.bridge.AppService.BotClient()
|
||||
|
||||
qrChan := make(chan string, 2)
|
||||
go func() {
|
||||
code := <-qrChan
|
||||
if code == "error" {
|
||||
return
|
||||
}
|
||||
qrCode, err := qrcode.Encode(code, qrcode.Low, 256)
|
||||
if err != nil {
|
||||
user.log.Errorln("Failed to encode QR code:", err)
|
||||
bot.SendNotice(roomID, "Failed to encode QR code (see logs for details)")
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := bot.UploadBytes(qrCode, "image/png")
|
||||
if err != nil {
|
||||
user.log.Errorln("Failed to upload QR code:", err)
|
||||
bot.SendNotice(roomID, "Failed to upload QR code (see logs for details)")
|
||||
return
|
||||
}
|
||||
|
||||
bot.SendImage(roomID, string(qrCode), resp.ContentURI)
|
||||
}()
|
||||
session, err := user.Conn.Login(qrChan)
|
||||
if err != nil {
|
||||
user.log.Warnln("Failed to log in:", err)
|
||||
bot.SendNotice(roomID, "Failed to log in: "+err.Error())
|
||||
qrChan <- "error"
|
||||
return
|
||||
}
|
||||
user.Session = &session
|
||||
user.Update()
|
||||
bot.SendNotice(roomID, "Successfully logged in. Synchronizing chats...")
|
||||
go user.Sync()
|
||||
}
|
||||
|
||||
func (user *User) Sync() {
|
||||
chats, err := user.Conn.Chats()
|
||||
if err != nil {
|
||||
user.log.Warnln("Failed to get chats")
|
||||
return
|
||||
}
|
||||
user.log.Debugln(chats)
|
||||
}
|
||||
|
||||
func (user *User) HandleError(err error) {
|
||||
user.log.Errorln("WhatsApp error:", err)
|
||||
fmt.Fprintf(os.Stderr, "%v", err)
|
||||
}
|
||||
|
||||
func (user *User) HandleTextMessage(message whatsapp.TextMessage) {
|
||||
fmt.Println(message)
|
||||
}
|
||||
|
||||
func (user *User) HandleImageMessage(message whatsapp.ImageMessage) {
|
||||
fmt.Println(message)
|
||||
}
|
||||
|
||||
func (user *User) HandleVideoMessage(message whatsapp.VideoMessage) {
|
||||
fmt.Println(message)
|
||||
}
|
||||
|
||||
func (user *User) HandleJsonMessage(message string) {
|
||||
fmt.Println(message)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue