mirror of
https://github.com/tulir/mautrix-whatsapp
synced 2024-12-14 09:23:51 +01:00
Add WhatsApp->Matrix read receipts and phone connection notifications
This commit is contained in:
parent
60529bf022
commit
1f87deb317
12 changed files with 178 additions and 69 deletions
|
@ -28,7 +28,7 @@
|
|||
* [x] Avatars
|
||||
* [ ] Presence
|
||||
* [ ] Typing notifications
|
||||
* [ ] Read receipts
|
||||
* [x] Read receipts
|
||||
* [ ] Admin/superadmin status
|
||||
* [ ] Membership actions
|
||||
* [ ] Invite
|
||||
|
|
|
@ -59,7 +59,7 @@ func (mq *MessageQuery) GetAll(owner types.MatrixUserID) (messages []*Message) {
|
|||
}
|
||||
|
||||
func (mq *MessageQuery) GetByJID(owner types.MatrixUserID, jid types.WhatsAppMessageID) *Message {
|
||||
return mq.get("SELECT * FROM message WHERE jid=?", jid)
|
||||
return mq.get("SELECT * FROM message WHERE owner=? AND jid=?", owner, jid)
|
||||
}
|
||||
|
||||
func (mq *MessageQuery) GetByMXID(mxid types.MatrixEventID) *Message {
|
||||
|
|
|
@ -93,7 +93,7 @@ func (puppet *Puppet) Insert() error {
|
|||
_, err := puppet.db.Exec("INSERT INTO puppet VALUES (?, ?, ?, ?)",
|
||||
puppet.JID, puppet.Receiver, puppet.Displayname, puppet.Avatar)
|
||||
if err != nil {
|
||||
puppet.log.Errorln("Failed to insert %s->%s: %v", puppet.JID, puppet.Receiver, err)
|
||||
puppet.log.Errorfln("Failed to insert %s->%s: %v", puppet.JID, puppet.Receiver, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ func (puppet *Puppet) Update() error {
|
|||
puppet.Displayname, puppet.Avatar,
|
||||
puppet.JID, puppet.Receiver)
|
||||
if err != nil {
|
||||
puppet.log.Errorln("Failed to update %s->%s: %v", puppet.JID, puppet.Receiver, err)
|
||||
puppet.log.Errorfln("Failed to update %s->%s: %v", puppet.JID, puppet.Receiver, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
77
portal.go
77
portal.go
|
@ -17,21 +17,24 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"maunium.net/go/mautrix-whatsapp/database"
|
||||
log "maunium.net/go/maulogger"
|
||||
"fmt"
|
||||
"maunium.net/go/mautrix-whatsapp/types"
|
||||
"maunium.net/go/gomatrix"
|
||||
"strings"
|
||||
"maunium.net/go/mautrix-appservice"
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
"sync"
|
||||
"net/http"
|
||||
"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
|
||||
"mime"
|
||||
"image"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"image"
|
||||
"math/rand"
|
||||
"mime"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
"maunium.net/go/gomatrix"
|
||||
"maunium.net/go/gomatrix/format"
|
||||
log "maunium.net/go/maulogger"
|
||||
"maunium.net/go/mautrix-appservice"
|
||||
"maunium.net/go/mautrix-whatsapp/database"
|
||||
"maunium.net/go/mautrix-whatsapp/types"
|
||||
"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
|
||||
)
|
||||
|
||||
func (user *User) GetPortalByMXID(mxid types.MatrixRoomID) *Portal {
|
||||
|
@ -221,7 +224,7 @@ func (portal *Portal) CreateMatrixRoom() error {
|
|||
name := portal.Name
|
||||
topic := portal.Topic
|
||||
isPrivateChat := false
|
||||
if strings.HasSuffix(portal.JID, "s.whatsapp.net") {
|
||||
if strings.HasSuffix(portal.JID, whatsapp_ext.NewUserSuffix) {
|
||||
puppet := portal.user.GetPuppetByJID(portal.JID)
|
||||
name = puppet.Displayname
|
||||
topic = "WhatsApp private chat"
|
||||
|
@ -244,7 +247,7 @@ func (portal *Portal) CreateMatrixRoom() error {
|
|||
}
|
||||
|
||||
func (portal *Portal) IsPrivateChat() bool {
|
||||
return strings.HasSuffix(portal.JID, puppetJIDStrippedSuffix)
|
||||
return strings.HasSuffix(portal.JID, whatsapp_ext.NewUserSuffix)
|
||||
}
|
||||
|
||||
func (portal *Portal) MainIntent() *appservice.IntentAPI {
|
||||
|
@ -282,13 +285,18 @@ func (portal *Portal) GetMessageIntent(info whatsapp.MessageInfo) *appservice.In
|
|||
return puppet.Intent()
|
||||
}
|
||||
|
||||
func (portal *Portal) GetRelations(info whatsapp.MessageInfo) (reply gomatrix.RelatesTo) {
|
||||
func (portal *Portal) SetReply(content *gomatrix.Content, info whatsapp.MessageInfo) {
|
||||
if len(info.QuotedMessageID) == 0 {
|
||||
return
|
||||
}
|
||||
message := portal.bridge.DB.Message.GetByJID(portal.Owner, info.QuotedMessageID)
|
||||
if message != nil {
|
||||
reply.InReplyTo.EventID = message.MXID
|
||||
event, err := portal.MainIntent().GetEvent(portal.MXID, message.MXID)
|
||||
if err != nil {
|
||||
portal.log.Warnln("Failed to get reply target:", err)
|
||||
return
|
||||
}
|
||||
content.SetReply(event)
|
||||
}
|
||||
return
|
||||
|
||||
|
@ -299,18 +307,24 @@ func (portal *Portal) HandleTextMessage(message whatsapp.TextMessage) {
|
|||
return
|
||||
}
|
||||
|
||||
portal.CreateMatrixRoom()
|
||||
err := portal.CreateMatrixRoom()
|
||||
if err != nil {
|
||||
portal.log.Errorln("Failed to create portal room:", err)
|
||||
return
|
||||
}
|
||||
|
||||
intent := portal.GetMessageIntent(message.Info)
|
||||
if intent == nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := intent.SendMassagedMessageEvent(portal.MXID, gomatrix.EventMessage, gomatrix.Content{
|
||||
content := gomatrix.Content{
|
||||
Body: message.Text,
|
||||
MsgType: gomatrix.MsgText,
|
||||
RelatesTo: portal.GetRelations(message.Info),
|
||||
}, int64(message.Info.Timestamp*1000))
|
||||
}
|
||||
portal.SetReply(&content, message.Info)
|
||||
|
||||
resp, err := intent.SendMassagedMessageEvent(portal.MXID, gomatrix.EventMessage, content, int64(message.Info.Timestamp*1000))
|
||||
if err != nil {
|
||||
portal.log.Errorfln("Failed to handle message %s: %v", message.Info.Id, err)
|
||||
return
|
||||
|
@ -324,7 +338,11 @@ func (portal *Portal) HandleMediaMessage(download func() ([]byte, error), thumbn
|
|||
return
|
||||
}
|
||||
|
||||
portal.CreateMatrixRoom()
|
||||
err := portal.CreateMatrixRoom()
|
||||
if err != nil {
|
||||
portal.log.Errorln("Failed to create portal room:", err)
|
||||
return
|
||||
}
|
||||
|
||||
intent := portal.GetMessageIntent(info)
|
||||
if intent == nil {
|
||||
|
@ -353,12 +371,12 @@ func (portal *Portal) HandleMediaMessage(download func() ([]byte, error), thumbn
|
|||
content := gomatrix.Content{
|
||||
Body: caption,
|
||||
URL: uploaded.ContentURI,
|
||||
Info: gomatrix.FileInfo{
|
||||
Info: &gomatrix.FileInfo{
|
||||
Size: len(data),
|
||||
MimeType: mimeType,
|
||||
},
|
||||
RelatesTo: portal.GetRelations(info),
|
||||
}
|
||||
portal.SetReply(&content, info)
|
||||
|
||||
if thumbnail != nil {
|
||||
thumbnailMime := http.DetectContentType(thumbnail)
|
||||
|
@ -422,6 +440,12 @@ var htmlParser = format.HTMLParser{
|
|||
},
|
||||
}
|
||||
|
||||
func makeMessageID() string {
|
||||
b := make([]byte, 10)
|
||||
rand.Read(b)
|
||||
return strings.ToUpper(hex.EncodeToString(b))
|
||||
}
|
||||
|
||||
func (portal *Portal) HandleMatrixMessage(evt *gomatrix.Event) {
|
||||
var err error
|
||||
switch evt.Content.MsgType {
|
||||
|
@ -430,18 +454,21 @@ func (portal *Portal) HandleMatrixMessage(evt *gomatrix.Event) {
|
|||
if evt.Content.Format == gomatrix.FormatHTML {
|
||||
text = htmlParser.Parse(evt.Content.FormattedBody)
|
||||
}
|
||||
id := makeMessageID()
|
||||
err = portal.user.Conn.Send(whatsapp.TextMessage{
|
||||
Text: text,
|
||||
Info: whatsapp.MessageInfo{
|
||||
Id: id,
|
||||
RemoteJid: portal.JID,
|
||||
},
|
||||
})
|
||||
portal.MarkHandled(id, evt.ID)
|
||||
default:
|
||||
portal.log.Debugln("Unhandled Matrix event:", evt)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
portal.log.Errorln("Error handling Matrix event %s: %v", evt.ID, err)
|
||||
portal.log.Errorfln("Error handling Matrix event %s: %v", evt.ID, err)
|
||||
} else {
|
||||
portal.log.Debugln("Handled Matrix event:", evt)
|
||||
}
|
||||
|
|
22
puppet.go
22
puppet.go
|
@ -17,18 +17,18 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"maunium.net/go/mautrix-whatsapp/database"
|
||||
log "maunium.net/go/maulogger"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"maunium.net/go/mautrix-whatsapp/types"
|
||||
"strings"
|
||||
"maunium.net/go/mautrix-appservice"
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
"net/http"
|
||||
)
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
const puppetJIDStrippedSuffix = "@s.whatsapp.net"
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
log "maunium.net/go/maulogger"
|
||||
"maunium.net/go/mautrix-appservice"
|
||||
"maunium.net/go/mautrix-whatsapp/database"
|
||||
"maunium.net/go/mautrix-whatsapp/types"
|
||||
"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
|
||||
)
|
||||
|
||||
func (bridge *Bridge) ParsePuppetMXID(mxid types.MatrixUserID) (types.MatrixUserID, types.WhatsAppID, bool) {
|
||||
userIDRegex, err := regexp.Compile(fmt.Sprintf("^@%s:%s$",
|
||||
|
@ -47,7 +47,7 @@ func (bridge *Bridge) ParsePuppetMXID(mxid types.MatrixUserID) (types.MatrixUser
|
|||
receiver = strings.Replace(receiver, "=40", "@", 1)
|
||||
colonIndex := strings.LastIndex(receiver, "=3")
|
||||
receiver = receiver[:colonIndex] + ":" + receiver[colonIndex+len("=3"):]
|
||||
jid := types.WhatsAppID(match[2] + puppetJIDStrippedSuffix)
|
||||
jid := types.WhatsAppID(match[2] + whatsapp_ext.NewUserSuffix)
|
||||
return receiver, jid, true
|
||||
}
|
||||
|
||||
|
@ -120,7 +120,7 @@ func (user *User) NewPuppet(dbPuppet *database.Puppet) *Puppet {
|
|||
dbPuppet.Receiver,
|
||||
strings.Replace(
|
||||
dbPuppet.JID,
|
||||
puppetJIDStrippedSuffix, "", 1)),
|
||||
whatsapp_ext.NewUserSuffix, "", 1)),
|
||||
user.bridge.Config.Homeserver.Domain),
|
||||
}
|
||||
}
|
||||
|
|
66
user.go
66
user.go
|
@ -17,14 +17,16 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"maunium.net/go/mautrix-whatsapp/database"
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
"time"
|
||||
"github.com/skip2/go-qrcode"
|
||||
log "maunium.net/go/maulogger"
|
||||
"maunium.net/go/mautrix-whatsapp/types"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
"github.com/skip2/go-qrcode"
|
||||
log "maunium.net/go/maulogger"
|
||||
"maunium.net/go/mautrix-whatsapp/database"
|
||||
"maunium.net/go/mautrix-whatsapp/types"
|
||||
"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
|
||||
)
|
||||
|
||||
|
@ -186,7 +188,7 @@ func (user *User) Sync() {
|
|||
user.log.Debugln("Syncing...")
|
||||
user.Conn.Contacts()
|
||||
for jid, contact := range user.Conn.Store.Contacts {
|
||||
if strings.HasSuffix(jid, puppetJIDStrippedSuffix) {
|
||||
if strings.HasSuffix(jid, whatsapp_ext.NewUserSuffix) {
|
||||
puppet := user.GetPuppetByJID(contact.Jid)
|
||||
puppet.Sync(contact)
|
||||
}
|
||||
|
@ -205,6 +207,10 @@ func (user *User) HandleError(err error) {
|
|||
user.log.Errorln("WhatsApp error:", err)
|
||||
}
|
||||
|
||||
func (user *User) HandleJSONParseError(err error) {
|
||||
user.log.Errorln("WhatsApp JSON parse error:", err)
|
||||
}
|
||||
|
||||
func (user *User) HandleTextMessage(message whatsapp.TextMessage) {
|
||||
user.log.Debugln("Received text message:", message)
|
||||
portal := user.GetPortalByJID(message.Info.RemoteJid)
|
||||
|
@ -231,6 +237,52 @@ func (user *User) HandleDocumentMessage(message whatsapp.DocumentMessage) {
|
|||
portal.HandleMediaMessage(message.Download, message.Thumbnail, message.Info, message.Type, message.Title)
|
||||
}
|
||||
|
||||
func (user *User) HandleStreamEvent(stream whatsapp_ext.StreamEvent) {
|
||||
if len(user.ManagementRoom) == 0 {
|
||||
return
|
||||
}
|
||||
switch stream.Type {
|
||||
case whatsapp_ext.StreamSleep:
|
||||
user.bridge.AppService.BotIntent().SendNotice(user.ManagementRoom, "WhatsApp client disconnected.")
|
||||
case whatsapp_ext.StreamUpdate:
|
||||
if user.Conn.Info != nil && user.Conn.Info.Phone != nil {
|
||||
user.bridge.AppService.BotIntent().SendNotice(user.ManagementRoom,
|
||||
fmt.Sprintf("WhatsApp v%s client connected from %s %s (OS v%s).",
|
||||
user.Conn.Info.Phone.WaVersion, user.Conn.Info.Phone.DeviceManufacturer, user.Conn.Info.Phone.DeviceModel, user.Conn.Info.Phone.OsVersion))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (user *User) HandleConnInfo(info whatsapp_ext.ConnInfo) {
|
||||
if len(user.ManagementRoom) > 0 && len(info.ProtocolVersion) > 0 {
|
||||
user.bridge.AppService.BotIntent().SendNotice(user.ManagementRoom,
|
||||
fmt.Sprintf("WhatsApp v%s client connected from %s %s (OS v%s).",
|
||||
info.Phone.WhatsAppVersion, info.Phone.DeviceManufacturer, info.Phone.DeviceModel, info.Phone.OSVersion))
|
||||
}
|
||||
}
|
||||
|
||||
func (user *User) HandleMsgInfo(info whatsapp_ext.MsgInfo) {
|
||||
if (info.Command == whatsapp_ext.MsgInfoCommandAck || info.Command == whatsapp_ext.MsgInfoCommandAcks) && info.Acknowledgement == whatsapp_ext.AckMessageRead {
|
||||
portal := user.GetPortalByJID(info.ToJID)
|
||||
if len(portal.MXID) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
intent := user.GetPuppetByJID(info.SenderJID).Intent()
|
||||
user.log.Debugln(info.IDs)
|
||||
for _, id := range info.IDs {
|
||||
msg := user.bridge.DB.Message.GetByJID(user.ID, id)
|
||||
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) HandleJsonMessage(message string) {
|
||||
user.log.Debugln("JSON message:", message)
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
package whatsapp_ext
|
||||
|
||||
import (
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
)
|
||||
|
||||
type ConnInfo struct {
|
||||
|
@ -26,8 +27,8 @@ type ConnInfo struct {
|
|||
BinaryVersion int `json:"binVersion"`
|
||||
Phone struct {
|
||||
WhatsAppVersion string `json:"wa_version"`
|
||||
MCC int `json:"mcc"`
|
||||
MNC int `json:"mnc"`
|
||||
MCC string `json:"mcc"`
|
||||
MNC string `json:"mnc"`
|
||||
OSVersion string `json:"os_version"`
|
||||
DeviceManufacturer string `json:"device_manufacturer"`
|
||||
DeviceModel string `json:"device_model"`
|
||||
|
|
|
@ -18,6 +18,7 @@ package whatsapp_ext
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
)
|
||||
|
||||
|
@ -27,6 +28,7 @@ type JSONMessageType string
|
|||
|
||||
const (
|
||||
MessageMsgInfo JSONMessageType = "MsgInfo"
|
||||
MessageMsg JSONMessageType = "Msg"
|
||||
MessagePresence JSONMessageType = "Presence"
|
||||
MessageStream JSONMessageType = "Stream"
|
||||
MessageConn JSONMessageType = "Conn"
|
||||
|
@ -76,11 +78,11 @@ func (ext *ExtendedConn) HandleJsonMessage(message string) {
|
|||
case MessageStream:
|
||||
ext.handleMessageStream(msg[1:])
|
||||
case MessageConn:
|
||||
ext.handleMessageProps(msg[1])
|
||||
ext.handleMessageConn(msg[1])
|
||||
case MessageProps:
|
||||
ext.handleMessageProps(msg[1])
|
||||
case MessageMsgInfo:
|
||||
ext.handleMessageMsgInfo(msg[1])
|
||||
case MessageMsgInfo, MessageMsg:
|
||||
ext.handleMessageMsgInfo(msgType, msg[1])
|
||||
default:
|
||||
for _, handler := range ext.handlers {
|
||||
ujmHandler, ok := handler.(UnhandledJSONMessageHandler)
|
||||
|
|
|
@ -17,21 +17,45 @@
|
|||
package whatsapp_ext
|
||||
|
||||
import (
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
)
|
||||
|
||||
type MsgInfoCommand string
|
||||
|
||||
const (
|
||||
MsgInfoCommandAcknowledge MsgInfoCommand = "ack"
|
||||
MsgInfoCommandAck MsgInfoCommand = "ack"
|
||||
MsgInfoCommandAcks MsgInfoCommand = "acks"
|
||||
)
|
||||
|
||||
type Acknowledgement int
|
||||
|
||||
const (
|
||||
AckMessageSent Acknowledgement = 1
|
||||
AckMessageDelivered Acknowledgement = 2
|
||||
AckMessageRead Acknowledgement = 3
|
||||
)
|
||||
|
||||
type JSONStringOrArray []string
|
||||
|
||||
func (jsoa *JSONStringOrArray) UnmarshalJSON(data []byte) error {
|
||||
var str string
|
||||
if json.Unmarshal(data, &str) == nil {
|
||||
*jsoa = []string{str}
|
||||
return nil
|
||||
}
|
||||
var strs []string
|
||||
json.Unmarshal(data, &strs)
|
||||
*jsoa = strs
|
||||
return nil
|
||||
}
|
||||
|
||||
type MsgInfo struct {
|
||||
Command MsgInfoCommand `json:"cmd"`
|
||||
ID string `json:"id"`
|
||||
Acknowledgement int `json:"ack"`
|
||||
IDs JSONStringOrArray `json:"id"`
|
||||
Acknowledgement Acknowledgement `json:"ack"`
|
||||
MessageFromJID string `json:"from"`
|
||||
SenderJID string `json:"participant"`
|
||||
ToJID string `json:"to"`
|
||||
|
@ -43,7 +67,7 @@ type MsgInfoHandler interface {
|
|||
HandleMsgInfo(MsgInfo)
|
||||
}
|
||||
|
||||
func (ext *ExtendedConn) handleMessageMsgInfo(message []byte) {
|
||||
func (ext *ExtendedConn) handleMessageMsgInfo(msgType JSONMessageType, message []byte) {
|
||||
var event MsgInfo
|
||||
err := json.Unmarshal(message, &event)
|
||||
if err != nil {
|
||||
|
@ -53,11 +77,14 @@ func (ext *ExtendedConn) handleMessageMsgInfo(message []byte) {
|
|||
event.MessageFromJID = strings.Replace(event.MessageFromJID, OldUserSuffix, NewUserSuffix, 1)
|
||||
event.SenderJID = strings.Replace(event.SenderJID, OldUserSuffix, NewUserSuffix, 1)
|
||||
event.ToJID = strings.Replace(event.ToJID, OldUserSuffix, NewUserSuffix, 1)
|
||||
if msgType == MessageMsg {
|
||||
event.SenderJID = event.MessageFromJID
|
||||
}
|
||||
for _, handler := range ext.handlers {
|
||||
msgInfoHandler, ok := handler.(MsgInfoHandler)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
msgInfoHandler.HandleMsgInfo(event)
|
||||
go msgInfoHandler.HandleMsgInfo(event)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,6 @@ func (ext *ExtendedConn) handleMessagePresence(message []byte) {
|
|||
if !ok {
|
||||
continue
|
||||
}
|
||||
presenceHandler.HandlePresence(event)
|
||||
go presenceHandler.HandlePresence(event)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,6 @@ func (ext *ExtendedConn) handleMessageProps(message []byte) {
|
|||
if !ok {
|
||||
continue
|
||||
}
|
||||
protocolPropsHandler.HandleProtocolProps(event)
|
||||
go protocolPropsHandler.HandleProtocolProps(event)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,6 @@ func (ext *ExtendedConn) handleMessageStream(message []json.RawMessage) {
|
|||
if !ok {
|
||||
continue
|
||||
}
|
||||
streamHandler.HandleStreamEvent(event)
|
||||
go streamHandler.HandleStreamEvent(event)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue