409
user.go
409
user.go
@@ -26,9 +26,6 @@ import (
|
||||
"time"
|
||||
|
||||
log "maunium.net/go/maulogger/v2"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/appservice"
|
||||
"maunium.net/go/mautrix/bridge"
|
||||
@@ -206,7 +203,7 @@ func (br *GMBridge) NewUser(dbUser *database.User) *User {
|
||||
user.PermissionLevel = user.bridge.Config.Bridge.Permissions.Get(user.MXID)
|
||||
user.Whitelisted = user.PermissionLevel >= bridgeconfig.PermissionLevelUser
|
||||
user.Admin = user.PermissionLevel >= bridgeconfig.PermissionLevelAdmin
|
||||
user.BridgeState = br.NewBridgeStateQueue(user, user.log)
|
||||
user.BridgeState = br.NewBridgeStateQueue(user)
|
||||
go user.handleMessageLoop()
|
||||
go user.runMessageRingBuffer()
|
||||
return user
|
||||
@@ -332,14 +329,6 @@ func (user *User) SetManagementRoom(roomID id.RoomID) {
|
||||
user.Update()
|
||||
}
|
||||
|
||||
func (user *User) SetSession(session *whatsapp.Session) {
|
||||
// user.Session = session
|
||||
// if session == nil {
|
||||
// user.LastConnection = 0
|
||||
// }
|
||||
// user.Update()
|
||||
}
|
||||
|
||||
func (user *User) Connect() bool {
|
||||
if user.Conn != nil {
|
||||
return true
|
||||
@@ -347,14 +336,14 @@ func (user *User) Connect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
user.log.Debugln("Connecting to GroupMe")
|
||||
user.log.Debugfln("Connecting to GroupMe")
|
||||
timeout := time.Duration(user.bridge.Config.GroupMe.ConnectionTimeout)
|
||||
if timeout == 0 {
|
||||
timeout = 20
|
||||
}
|
||||
conn := groupme.NewPushSubscription(context.Background())
|
||||
user.Conn = &conn
|
||||
user.Conn.StartListening(context.TODO(), groupmeext.NewFayeClient(user.log))
|
||||
user.Conn.StartListening(context.Background(), groupmeext.NewFayeClient(user.log))
|
||||
user.Conn.AddFullHandler(user)
|
||||
|
||||
//TODO: typing notification?
|
||||
@@ -737,112 +726,7 @@ func (user *User) UpdateDirectChats(chats map[id.UserID][]id.RoomID) {
|
||||
}
|
||||
}
|
||||
|
||||
func (user *User) HandleContactList(contacts []whatsapp.Contact) {
|
||||
contactMap := make(map[string]whatsapp.Contact)
|
||||
for _, contact := range contacts {
|
||||
contactMap[contact.Jid] = contact
|
||||
}
|
||||
go user.syncPuppets(contactMap)
|
||||
}
|
||||
|
||||
func (user *User) syncPuppets(contacts map[string]whatsapp.Contact) {
|
||||
// if contacts == nil {
|
||||
// contacts = user.Conn.Store.Contacts
|
||||
// }
|
||||
// user.log.Infoln("Syncing puppet info from contacts")
|
||||
// for jid, contact := range contacts {
|
||||
// if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) {
|
||||
// puppet := user.bridge.GetPuppetByJID(contact.Jid)
|
||||
// puppet.Sync(user, contact)
|
||||
// }
|
||||
// }
|
||||
// user.log.Infoln("Finished syncing puppet info from contacts")
|
||||
}
|
||||
|
||||
func (user *User) updateLastConnectionIfNecessary() {
|
||||
// if user.LastConnection+60 < uint64(time.Now().Unix()) {
|
||||
// user.UpdateLastConnection()
|
||||
// }
|
||||
}
|
||||
|
||||
func (user *User) HandleError(err error) {
|
||||
if !errors.Is(err, whatsapp.ErrInvalidWsData) {
|
||||
user.log.Errorfln("WhatsApp error: %v", err)
|
||||
}
|
||||
if closed, ok := err.(*whatsapp.ErrConnectionClosed); ok {
|
||||
user.bridge.Metrics.TrackDisconnection(user.MXID)
|
||||
if closed.Code == 1000 && user.cleanDisconnection {
|
||||
user.bridge.Metrics.TrackConnectionState(user.GMID, false)
|
||||
user.cleanDisconnection = false
|
||||
user.log.Infoln("Clean disconnection by server")
|
||||
return
|
||||
}
|
||||
go user.tryReconnect(fmt.Sprintf("Your WhatsApp connection was closed with websocket status code %d", closed.Code))
|
||||
} else if failed, ok := err.(*whatsapp.ErrConnectionFailed); ok {
|
||||
user.bridge.Metrics.TrackDisconnection(user.MXID)
|
||||
user.ConnectionErrors++
|
||||
go user.tryReconnect(fmt.Sprintf("Your WhatsApp connection failed: %v", failed.Err))
|
||||
}
|
||||
// Otherwise unknown error, probably mostly harmless
|
||||
}
|
||||
|
||||
func (user *User) tryReconnect(msg string) {
|
||||
// user.bridge.Metrics.TrackConnectionState(user.JID, false)
|
||||
// if user.ConnectionErrors > user.bridge.Config.Bridge.MaxConnectionAttempts {
|
||||
// user.sendMarkdownBridgeAlert("%s. Use the `reconnect` command to reconnect.", msg)
|
||||
// return
|
||||
// }
|
||||
// if user.bridge.Config.Bridge.ReportConnectionRetry {
|
||||
// user.sendBridgeNotice("%s. Reconnecting...", msg)
|
||||
// // Don't want the same error to be repeated
|
||||
// msg = ""
|
||||
// }
|
||||
// var tries uint
|
||||
// var exponentialBackoff bool
|
||||
// baseDelay := time.Duration(user.bridge.Config.Bridge.ConnectionRetryDelay)
|
||||
// if baseDelay < 0 {
|
||||
// exponentialBackoff = true
|
||||
// baseDelay = -baseDelay + 1
|
||||
// }
|
||||
// delay := baseDelay
|
||||
// for user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
|
||||
// err := user.Conn.Restore()
|
||||
// if err == nil {
|
||||
// user.ConnectionErrors = 0
|
||||
// if user.bridge.Config.Bridge.ReportConnectionRetry {
|
||||
// user.sendBridgeNotice("Reconnected successfully")
|
||||
// }
|
||||
// user.PostLogin()
|
||||
// return
|
||||
// } else if err.Error() == "init responded with 400" {
|
||||
// user.log.Infoln("Got init 400 error when trying to reconnect, resetting connection...")
|
||||
// sess, err := user.Conn.Disconnect()
|
||||
// if err != nil {
|
||||
// user.log.Debugln("Error while disconnecting for connection reset:", err)
|
||||
// }
|
||||
// if len(sess.Wid) > 0 {
|
||||
// user.SetSession(&sess)
|
||||
// }
|
||||
// }
|
||||
// user.log.Errorln("Error while trying to reconnect after disconnection:", err)
|
||||
// tries++
|
||||
// user.ConnectionErrors++
|
||||
// if user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
|
||||
// if exponentialBackoff {
|
||||
// delay = (1 << tries) + baseDelay
|
||||
// }
|
||||
// if user.bridge.Config.Bridge.ReportConnectionRetry {
|
||||
// user.sendBridgeNotice("Reconnection attempt failed: %v. Retrying in %d seconds...", err, delay)
|
||||
// }
|
||||
// time.Sleep(delay * time.Second)
|
||||
// }
|
||||
// }
|
||||
|
||||
// if user.bridge.Config.Bridge.ReportConnectionRetry {
|
||||
// user.sendMarkdownBridgeAlert("%d reconnection attempts failed. Use the `reconnect` command to try to reconnect manually.", tries)
|
||||
// } else {
|
||||
// user.sendMarkdownBridgeAlert("\u26a0 %s. Additionally, %d reconnection attempts failed. Use the `reconnect` command to try to reconnect.", msg, tries)
|
||||
// }
|
||||
}
|
||||
|
||||
func (user *User) ShouldCallSynchronously() bool {
|
||||
@@ -850,7 +734,7 @@ func (user *User) ShouldCallSynchronously() bool {
|
||||
}
|
||||
|
||||
func (user *User) HandleJSONParseError(err error) {
|
||||
user.log.Errorln("WhatsApp JSON parse error:", err)
|
||||
user.log.Errorln("GroupMe JSON parse error:", err)
|
||||
}
|
||||
|
||||
func (user *User) PortalKey(gmid groupme.ID) database.PortalKey {
|
||||
@@ -893,34 +777,6 @@ func (user *User) handleMessageLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
//func (user *User) HandleNewContact(contact whatsapp.Contact) {
|
||||
// user.log.Debugfln("Contact message: %+v", contact)
|
||||
// go func() {
|
||||
// if strings.HasSuffix(contact.Jid, whatsappExt.OldUserSuffix) {
|
||||
// contact.Jid = strings.Replace(contact.Jid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, -1)
|
||||
// }
|
||||
// puppet := user.bridge.GetPuppetByJID(contact.Jid)
|
||||
// puppet.UpdateName(user, contact)
|
||||
// }()
|
||||
//}
|
||||
|
||||
//func (user *User) HandleBatteryMessage(battery whatsapp.BatteryMessage) {
|
||||
// user.log.Debugfln("Battery message: %+v", battery)
|
||||
// var notice string
|
||||
// if !battery.Plugged && battery.Percentage < 15 && user.batteryWarningsSent < 1 {
|
||||
// notice = fmt.Sprintf("Phone battery low (%d %% remaining)", battery.Percentage)
|
||||
// user.batteryWarningsSent = 1
|
||||
// } else if !battery.Plugged && battery.Percentage < 5 && user.batteryWarningsSent < 2 {
|
||||
// notice = fmt.Sprintf("Phone battery very low (%d %% remaining)", battery.Percentage)
|
||||
// user.batteryWarningsSent = 2
|
||||
// } else if battery.Percentage > 15 || battery.Plugged {
|
||||
// user.batteryWarningsSent = 0
|
||||
// }
|
||||
// if notice != "" {
|
||||
// go user.sendBridgeNotice("%s", notice)
|
||||
// }
|
||||
//}
|
||||
|
||||
func (user *User) HandleTextMessage(message groupme.Message) {
|
||||
id := database.ParsePortalKey(message.GroupID.String())
|
||||
|
||||
@@ -983,270 +839,15 @@ func (user *User) HandleNewNickname(groupID, userID groupme.ID, name string) {
|
||||
|
||||
func (user *User) HandleNewAvatarInGroup(groupID, userID groupme.ID, url string) {
|
||||
puppet := user.bridge.GetPuppetByGMID(userID)
|
||||
if puppet != nil {
|
||||
puppet.UpdateAvatar(user, false)
|
||||
}
|
||||
puppet.UpdateAvatar(user, false)
|
||||
}
|
||||
|
||||
func (user *User) HandleMembers(_ groupme.ID, _ []groupme.Member, _ bool) {
|
||||
user.HandleChatList()
|
||||
}
|
||||
|
||||
//func (user *User) HandleImageMessage(message whatsapp.ImageMessage) {
|
||||
// user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
||||
//}
|
||||
//
|
||||
//func (user *User) HandleStickerMessage(message whatsapp.StickerMessage) {
|
||||
// user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
||||
//}
|
||||
//
|
||||
//func (user *User) HandleVideoMessage(message whatsapp.VideoMessage) {
|
||||
// user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
||||
//}
|
||||
//
|
||||
//func (user *User) HandleAudioMessage(message whatsapp.AudioMessage) {
|
||||
// user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
||||
//}
|
||||
//
|
||||
//func (user *User) HandleDocumentMessage(message whatsapp.DocumentMessage) {
|
||||
// user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
||||
//}
|
||||
//
|
||||
//func (user *User) HandleContactMessage(message whatsapp.ContactMessage) {
|
||||
// user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
||||
//}
|
||||
//
|
||||
//func (user *User) HandleLocationMessage(message whatsapp.LocationMessage) {
|
||||
// user.messageInput <- PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp}
|
||||
//}
|
||||
//
|
||||
//func (user *User) HandleMessageRevoke(message whatsappExt.MessageRevocation) {
|
||||
// user.messageInput <- PortalMessage{message.RemoteJid, user, message, 0}
|
||||
//}
|
||||
|
||||
type FakeMessage struct {
|
||||
Text string
|
||||
ID string
|
||||
Alert bool
|
||||
}
|
||||
|
||||
//func (user *User) HandleCallInfo(info whatsappExt.CallInfo) {
|
||||
// if info.Data != nil {
|
||||
// return
|
||||
// }
|
||||
// data := FakeMessage{
|
||||
// ID: info.ID,
|
||||
// }
|
||||
// switch info.Type {
|
||||
// case whatsappExt.CallOffer:
|
||||
// if !user.bridge.Config.Bridge.CallNotices.Start {
|
||||
// return
|
||||
// }
|
||||
// data.Text = "Incoming call"
|
||||
// data.Alert = true
|
||||
// case whatsappExt.CallOfferVideo:
|
||||
// if !user.bridge.Config.Bridge.CallNotices.Start {
|
||||
// return
|
||||
// }
|
||||
// data.Text = "Incoming video call"
|
||||
// data.Alert = true
|
||||
// case whatsappExt.CallTerminate:
|
||||
// if !user.bridge.Config.Bridge.CallNotices.End {
|
||||
// return
|
||||
// }
|
||||
// data.Text = "Call ended"
|
||||
// data.ID += "E"
|
||||
// default:
|
||||
// return
|
||||
// }
|
||||
// portal := user.GetPortalByJID(info.From)
|
||||
// if portal != nil {
|
||||
// portal.messages <- PortalMessage{info.From, user, data, 0}
|
||||
// }
|
||||
//}
|
||||
|
||||
//func (user *User) HandlePresence(info whatsappExt.Presence) {
|
||||
// puppet := user.bridge.GetPuppetByJID(info.SenderJID)
|
||||
// switch info.Status {
|
||||
// case whatsapp.PresenceUnavailable:
|
||||
// _ = puppet.DefaultIntent().SetPresence("offline")
|
||||
// case whatsapp.PresenceAvailable:
|
||||
// if len(puppet.typingIn) > 0 && puppet.typingAt+15 > time.Now().Unix() {
|
||||
// portal := user.bridge.GetPortalByMXID(puppet.typingIn)
|
||||
// _, _ = puppet.IntentFor(portal).UserTyping(puppet.typingIn, false, 0)
|
||||
// puppet.typingIn = ""
|
||||
// puppet.typingAt = 0
|
||||
// } else {
|
||||
// _ = puppet.DefaultIntent().SetPresence("online")
|
||||
// }
|
||||
// case whatsapp.PresenceComposing:
|
||||
// portal := user.GetPortalByJID(info.JID)
|
||||
// if len(puppet.typingIn) > 0 && puppet.typingAt+15 > time.Now().Unix() {
|
||||
// if puppet.typingIn == portal.MXID {
|
||||
// return
|
||||
// }
|
||||
// _, _ = puppet.IntentFor(portal).UserTyping(puppet.typingIn, false, 0)
|
||||
// }
|
||||
// puppet.typingIn = portal.MXID
|
||||
// puppet.typingAt = time.Now().Unix()
|
||||
// _, _ = puppet.IntentFor(portal).UserTyping(portal.MXID, true, 15*1000)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (user *User) HandleMsgInfo(info whatsappExt.MsgInfo) {
|
||||
// if (info.Command == whatsappExt.MsgInfoCommandAck || info.Command == whatsappExt.MsgInfoCommandAcks) && info.Acknowledgement == whatsappExt.AckMessageRead {
|
||||
// portal := user.GetPortalByJID(info.ToJID)
|
||||
// if len(portal.MXID) == 0 {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// go func() {
|
||||
// intent := user.bridge.GetPuppetByJID(info.SenderJID).IntentFor(portal)
|
||||
// for _, msgID := range info.IDs {
|
||||
// msg := user.bridge.DB.Message.GetByJID(portal.Key, msgID)
|
||||
// if msg == nil {
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// err := intent.MarkRead(portal.MXID, msg.MXID)
|
||||
// if err != nil {
|
||||
// user.log.Warnln("Failed to mark message %s as read by %s: %v", msg.MXID, info.SenderJID, err)
|
||||
// }
|
||||
// }
|
||||
// }()
|
||||
// }
|
||||
//}
|
||||
|
||||
//func (user *User) HandleReceivedMessage(received whatsapp.ReceivedMessage) {
|
||||
// if received.Type == "read" {
|
||||
// user.markSelfRead(received.Jid, received.Index)
|
||||
// } else {
|
||||
// user.log.Debugfln("Unknown received message type: %+v", received)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (user *User) HandleReadMessage(read whatsapp.ReadMessage) {
|
||||
// user.log.Debugfln("Received chat read message: %+v", read)
|
||||
// user.markSelfRead(read.Jid, "")
|
||||
//}
|
||||
//
|
||||
//func (user *User) markSelfRead(jid, messageID string) {
|
||||
// if strings.HasSuffix(jid, whatsappExt.OldUserSuffix) {
|
||||
// jid = strings.Replace(jid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, -1)
|
||||
// }
|
||||
// puppet := user.bridge.GetPuppetByJID(user.JID)
|
||||
// if puppet == nil {
|
||||
// return
|
||||
// }
|
||||
// intent := puppet.CustomIntent()
|
||||
// if intent == nil {
|
||||
// return
|
||||
// }
|
||||
// portal := user.GetPortalByJID(jid)
|
||||
// if portal == nil {
|
||||
// return
|
||||
// }
|
||||
// var message *database.Message
|
||||
// if messageID == "" {
|
||||
// message = user.bridge.DB.Message.GetLastInChat(portal.Key)
|
||||
// if message == nil {
|
||||
// return
|
||||
// }
|
||||
// user.log.Debugfln("User read chat %s/%s in WhatsApp mobile (last known event: %s/%s)", portal.Key.JID, portal.MXID, message.JID, message.MXID)
|
||||
// } else {
|
||||
// message = user.bridge.DB.Message.GetByJID(portal.Key, messageID)
|
||||
// if message == nil {
|
||||
// return
|
||||
// }
|
||||
// user.log.Debugfln("User read message %s/%s in %s/%s in WhatsApp mobile", message.JID, message.MXID, portal.Key.JID, portal.MXID)
|
||||
// }
|
||||
// err := intent.MarkRead(portal.MXID, message.MXID)
|
||||
// if err != nil {
|
||||
// user.log.Warnfln("Failed to bridge own read receipt in %s: %v", jid, err)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (user *User) HandleCommand(cmd whatsappExt.Command) {
|
||||
// switch cmd.Type {
|
||||
// case whatsappExt.CommandPicture:
|
||||
// if strings.HasSuffix(cmd.JID, whatsappExt.NewUserSuffix) {
|
||||
// puppet := user.bridge.GetPuppetByJID(cmd.JID)
|
||||
// go puppet.UpdateAvatar(user, cmd.ProfilePicInfo)
|
||||
// } else {
|
||||
// portal := user.GetPortalByJID(cmd.JID)
|
||||
// go portal.UpdateAvatar(user, cmd.ProfilePicInfo, true)
|
||||
// }
|
||||
// case whatsappExt.CommandDisconnect:
|
||||
// user.cleanDisconnection = true
|
||||
// if cmd.Kind == "replaced" {
|
||||
// go user.sendMarkdownBridgeAlert("\u26a0 Your WhatsApp connection was closed by the server because you opened another WhatsApp Web client.\n\n" +
|
||||
// "Use the `reconnect` command to disconnect the other client and resume bridging.")
|
||||
// } else {
|
||||
// user.log.Warnln("Unknown kind of disconnect:", string(cmd.Raw))
|
||||
// go user.sendMarkdownBridgeAlert("\u26a0 Your WhatsApp connection was closed by the server (reason code: %s).\n\n"+
|
||||
// "Use the `reconnect` command to reconnect.", cmd.Kind)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) {
|
||||
// if cmd.Command != whatsappExt.ChatUpdateCommandAction {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// portal := user.GetPortalByJID(cmd.JID)
|
||||
// if len(portal.MXID) == 0 {
|
||||
// if cmd.Data.Action == whatsappExt.ChatActionIntroduce || cmd.Data.Action == whatsappExt.ChatActionCreate {
|
||||
// go func() {
|
||||
// err := portal.CreateMatrixRoom(user)
|
||||
// if err != nil {
|
||||
// user.log.Errorln("Failed to create portal room after receiving join event:", err)
|
||||
// }
|
||||
// }()
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// switch cmd.Data.Action {
|
||||
// case whatsappExt.ChatActionNameChange:
|
||||
// go portal.UpdateName(cmd.Data.NameChange.Name, cmd.Data.SenderJID, true)
|
||||
// case whatsappExt.ChatActionAddTopic:
|
||||
// go portal.UpdateTopic(cmd.Data.AddTopic.Topic, cmd.Data.SenderJID, true)
|
||||
// case whatsappExt.ChatActionRemoveTopic:
|
||||
// go portal.UpdateTopic("", cmd.Data.SenderJID, true)
|
||||
// case whatsappExt.ChatActionPromote:
|
||||
// go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, true)
|
||||
// case whatsappExt.ChatActionDemote:
|
||||
// go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, false)
|
||||
// case whatsappExt.ChatActionAnnounce:
|
||||
// go portal.RestrictMessageSending(cmd.Data.Announce)
|
||||
// case whatsappExt.ChatActionRestrict:
|
||||
// go portal.RestrictMetadataChanges(cmd.Data.Restrict)
|
||||
// case whatsappExt.ChatActionRemove:
|
||||
// go portal.HandleWhatsAppKick(cmd.Data.SenderJID, cmd.Data.UserChange.JIDs)
|
||||
// case whatsappExt.ChatActionAdd:
|
||||
// go portal.HandleWhatsAppInvite(cmd.Data.SenderJID, cmd.Data.UserChange.JIDs)
|
||||
// //case whatsappExt.ChatActionIntroduce:
|
||||
// // if cmd.Data.SenderJID != "unknown" {
|
||||
// // go portal.Sync(user, whatsapp.Contact{Jid: portal.Key.JID})
|
||||
// // }
|
||||
// }
|
||||
//}
|
||||
|
||||
//func (user *User) HandleJsonMessage(message string) {
|
||||
// var msg json.RawMessage
|
||||
// err := json.Unmarshal([]byte(message), &msg)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// user.log.Debugln("JSON message:", message)
|
||||
// user.updateLastConnectionIfNecessary()
|
||||
//}
|
||||
|
||||
//func (user *User) HandleRawMessage(message *waProto.WebMessageInfo) {
|
||||
// user.updateLastConnectionIfNecessary()
|
||||
//}
|
||||
//
|
||||
//func (user *User) HandleUnknownBinaryNode(node *waBinary.Node) {
|
||||
// user.log.Debugfln("Unknown binary message: %+v", node)
|
||||
//}
|
||||
|
||||
Reference in New Issue
Block a user