emoji signaling

This commit is contained in:
Aine
2023-10-19 10:31:14 +03:00
parent d3aaa5c060
commit f2e032e1e8
13 changed files with 170 additions and 42 deletions

View File

@@ -608,8 +608,8 @@ func (b *Bot) runSendCommand(ctx context.Context, cfg config.Room, tos []string,
} }
} }
b.mu.Lock(evt.RoomID.String()) b.lock(evt.RoomID, evt.ID)
defer b.mu.Unlock(evt.RoomID.String()) defer b.unlock(evt.RoomID, evt.ID)
domain := utils.SanitizeDomain(cfg.Domain()) domain := utils.SanitizeDomain(cfg.Domain())
from := cfg.Mailbox() + "@" + domain from := cfg.Mailbox() + "@" + domain

View File

@@ -56,15 +56,19 @@ func (b *Bot) shouldQueue(msg string) bool {
// Sendmail tries to send email immediately, but if it gets 4xx error (greylisting), // Sendmail tries to send email immediately, but if it gets 4xx error (greylisting),
// the email will be added to the queue and retried several times after that // the email will be added to the queue and retried several times after that
func (b *Bot) Sendmail(eventID id.EventID, from, to, data string) (bool, error) { func (b *Bot) Sendmail(eventID id.EventID, from, to, data string) (bool, error) {
log := b.log.With().Str("from", from).Str("to", to).Str("eventID", eventID.String()).Logger()
log.Info().Msg("attempting to deliver email")
err := b.sendmail(from, to, data) err := b.sendmail(from, to, data)
if err != nil { if err != nil {
if b.shouldQueue(err.Error()) { if b.shouldQueue(err.Error()) {
b.log.Info().Err(err).Str("id", eventID.String()).Str("from", from).Str("to", to).Msg("email has been added to the queue") log.Info().Err(err).Msg("email has been added to the queue")
return true, b.q.Add(eventID.String(), from, to, data) return true, b.q.Add(eventID.String(), from, to, data)
} }
log.Warn().Err(err).Msg("email delivery failed")
return false, err return false, err
} }
log.Warn().Err(err).Msg("email delivery succeeded")
return false, nil return false, nil
} }
@@ -286,8 +290,8 @@ func (b *Bot) SendEmailReply(ctx context.Context) {
return return
} }
b.mu.Lock(evt.RoomID.String()) b.lock(evt.RoomID, evt.ID)
defer b.mu.Unlock(evt.RoomID.String()) defer b.unlock(evt.RoomID, evt.ID)
meta := b.getParentEmail(evt, mailbox) meta := b.getParentEmail(evt, mailbox)

29
bot/mutex.go Normal file
View File

@@ -0,0 +1,29 @@
package bot
import (
"maunium.net/go/mautrix/id"
)
func (b *Bot) lock(roomID id.RoomID, optionalEventID ...id.EventID) {
b.mu.Lock(roomID.String())
if len(optionalEventID) == 0 {
return
}
evtID := optionalEventID[0]
if _, err := b.lp.GetClient().SendReaction(roomID, evtID, "📨"); err != nil {
b.log.Error().Err(err).Str("roomID", roomID.String()).Str("eventID", evtID.String()).Msg("cannot send reaction on lock")
}
}
func (b *Bot) unlock(roomID id.RoomID, optionalEventID ...id.EventID) {
b.mu.Unlock(roomID.String())
if len(optionalEventID) == 0 {
return
}
evtID := optionalEventID[0]
if _, err := b.lp.GetClient().SendReaction(roomID, evtID, "✅"); err != nil {
b.log.Error().Err(err).Str("roomID", roomID.String()).Str("eventID", evtID.String()).Msg("cannot send reaction on unlock")
}
}

2
go.mod
View File

@@ -26,7 +26,7 @@ require (
gitlab.com/etke.cc/go/secgen v1.1.1 gitlab.com/etke.cc/go/secgen v1.1.1
gitlab.com/etke.cc/go/trysmtp v1.1.3 gitlab.com/etke.cc/go/trysmtp v1.1.3
gitlab.com/etke.cc/go/validator v1.0.6 gitlab.com/etke.cc/go/validator v1.0.6
gitlab.com/etke.cc/linkpearl v0.0.0-20230929133006-84554ee97edb gitlab.com/etke.cc/linkpearl v0.0.0-20231007103859-01907e2b75f2
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/exp v0.0.0-20230905200255-921286631fa9
maunium.net/go/mautrix v0.16.1 maunium.net/go/mautrix v0.16.1
) )

4
go.sum
View File

@@ -111,8 +111,8 @@ gitlab.com/etke.cc/go/trysmtp v1.1.3 h1:e2EHond77onMaecqCg6mWumffTSEf+ycgj88nbee
gitlab.com/etke.cc/go/trysmtp v1.1.3/go.mod h1:lOO7tTdAE0a3ETV3wN3GJ7I1Tqewu7YTpPWaOmTteV0= gitlab.com/etke.cc/go/trysmtp v1.1.3/go.mod h1:lOO7tTdAE0a3ETV3wN3GJ7I1Tqewu7YTpPWaOmTteV0=
gitlab.com/etke.cc/go/validator v1.0.6 h1:w0Muxf9Pqw7xvF7NaaswE6d7r9U3nB2t2l5PnFMrecQ= gitlab.com/etke.cc/go/validator v1.0.6 h1:w0Muxf9Pqw7xvF7NaaswE6d7r9U3nB2t2l5PnFMrecQ=
gitlab.com/etke.cc/go/validator v1.0.6/go.mod h1:Id0SxRj0J3IPhiKlj0w1plxVLZfHlkwipn7HfRZsDts= gitlab.com/etke.cc/go/validator v1.0.6/go.mod h1:Id0SxRj0J3IPhiKlj0w1plxVLZfHlkwipn7HfRZsDts=
gitlab.com/etke.cc/linkpearl v0.0.0-20230929133006-84554ee97edb h1:QoabJtxrQg7P2kuxwOp3iU6uKoep+5QQAtMomajXcpA= gitlab.com/etke.cc/linkpearl v0.0.0-20231007103859-01907e2b75f2 h1:QPbGnfbrGdDT8NtxZuwzRQou5I62b/mvesmf5iXClPc=
gitlab.com/etke.cc/linkpearl v0.0.0-20230929133006-84554ee97edb/go.mod h1:IZ0TE+ZnIdJLb538owDMxhtpWH7blfW+oR7e5XRXxNY= gitlab.com/etke.cc/linkpearl v0.0.0-20231007103859-01907e2b75f2/go.mod h1:IZ0TE+ZnIdJLb538owDMxhtpWH7blfW+oR7e5XRXxNY=
go.mau.fi/util v0.1.0 h1:BwIFWIOEeO7lsiI2eWKFkWTfc5yQmoe+0FYyOFVyaoE= go.mau.fi/util v0.1.0 h1:BwIFWIOEeO7lsiI2eWKFkWTfc5yQmoe+0FYyOFVyaoE=
go.mau.fi/util v0.1.0/go.mod h1:AxuJUMCxpzgJ5eV9JbPWKRH8aAJJidxetNdUj7qcb84= go.mau.fi/util v0.1.0/go.mod h1:AxuJUMCxpzgJ5eV9JbPWKRH8aAJJidxetNdUj7qcb84=
golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=

View File

@@ -29,40 +29,43 @@ func newClient(cfg *RelayConfig, log *zerolog.Logger) *Client {
// Send email // Send email
func (c Client) Send(from, to, data string) error { func (c Client) Send(from, to, data string) error {
c.log.Debug().Str("from", from).Str("to", to).Msg("sending email") log := c.log.With().Str("from", from).Str("to", to).Logger()
log.Debug().Msg("sending email")
var conn *smtp.Client var conn *smtp.Client
var err error var err error
if c.config.Host != "" { if c.config.Host != "" {
log.Debug().Msg("creating relay client...")
conn, err = c.createDirectClient(from, to) conn, err = c.createDirectClient(from, to)
} else { } else {
log.Debug().Msg("trying direct SMTP connection...")
conn, err = trysmtp.Connect(from, to) conn, err = trysmtp.Connect(from, to)
} }
if conn == nil { if conn == nil {
c.log.Error().Err(err).Str("server_of", to).Msg("cannot connect to SMTP server") log.Error().Err(err).Str("server_of", to).Msg("cannot connect to SMTP server")
return err return err
} }
if err != nil { if err != nil {
c.log.Warn().Err(err).Str("server_of", to).Msg("connection to the SMTP server returned non-fatal error(-s)") log.Warn().Err(err).Str("server_of", to).Msg("connection to the SMTP server returned non-fatal error(-s)")
} }
defer conn.Close() defer conn.Close()
var w io.WriteCloser var w io.WriteCloser
w, err = conn.Data() w, err = conn.Data()
if err != nil { if err != nil {
c.log.Error().Err(err).Msg("cannot send DATA command") log.Error().Err(err).Msg("cannot send DATA command")
return err return err
} }
defer w.Close() defer w.Close()
c.log.Debug().Str("DATA", data).Msg("sending command") c.log.Debug().Str("DATA", data).Msg("sending command")
_, err = strings.NewReader(data).WriteTo(w) _, err = strings.NewReader(data).WriteTo(w)
if err != nil { if err != nil {
c.log.Error().Err(err).Msg("cannot write DATA") log.Error().Err(err).Msg("cannot write DATA")
return err return err
} }
c.log.Debug().Msg("email has been sent") log.Debug().Msg("email has been sent")
return nil return nil
} }

View File

@@ -39,6 +39,9 @@ type Config struct {
// MaxRetries for operations like auto join // MaxRetries for operations like auto join
MaxRetries int MaxRetries int
// EventsLimit for methods like lp.Threads() or lp.FindEventBy()
EventsLimit int
// Logger // Logger
Logger zerolog.Logger Logger zerolog.Logger

73
vendor/gitlab.com/etke.cc/linkpearl/events.go generated vendored Normal file
View File

@@ -0,0 +1,73 @@
package linkpearl
import (
"strconv"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// RespThreads is response of https://spec.matrix.org/v1.8/client-server-api/#get_matrixclientv1roomsroomidthreads
type RespThreads struct {
Chunk []*event.Event `json:"chunk"`
NextBatch string `json:"next_batch"`
}
// Threads endpoint, ref: https://spec.matrix.org/v1.8/client-server-api/#get_matrixclientv1roomsroomidthreads
func (l *Linkpearl) Threads(roomID id.RoomID, fromToken ...string) (*RespThreads, error) {
var from string
if len(fromToken) > 0 {
from = fromToken[0]
}
query := map[string]string{
"from": from,
"limit": strconv.Itoa(l.eventsLimit),
}
var resp *RespThreads
urlPath := l.GetClient().BuildURLWithQuery(mautrix.ClientURLPath{"v1", "rooms", roomID, "threads"}, query)
_, err := l.GetClient().MakeRequest("GET", urlPath, nil, &resp)
return resp, UnwrapError(err)
}
// FindEventBy tries to find event by field and value
func (l *Linkpearl) FindEventBy(roomID id.RoomID, field, value string, fromToken ...string) *event.Event {
var from string
if len(fromToken) > 0 {
from = fromToken[0]
}
resp, err := l.GetClient().Messages(roomID, from, "", mautrix.DirectionBackward, nil, l.eventsLimit)
err = UnwrapError(err)
if err != nil {
l.log.Warn().Err(err).Str("roomID", roomID.String()).Msg("cannot get room events")
return nil
}
for _, msg := range resp.Chunk {
evt, contains := l.eventContains(msg, field, value)
if contains {
return evt
}
}
if resp.End == "" { // nothing more
return nil
}
return l.FindEventBy(roomID, field, value, resp.End)
}
func (l *Linkpearl) eventContains(evt *event.Event, field, value string) (*event.Event, bool) {
if evt.Type == event.EventEncrypted {
ParseContent(evt, &l.log)
decrypted, err := l.GetClient().Crypto.Decrypt(evt)
if err == nil {
evt = decrypted
}
}
return evt, EventContains(evt, field, value)
}

View File

@@ -18,6 +18,8 @@ const (
DefaultMaxRetries = 10 DefaultMaxRetries = 10
// DefaultAccountDataCache size // DefaultAccountDataCache size
DefaultAccountDataCache = 1000 DefaultAccountDataCache = 1000
// DefaultEventsLimit for methods like lp.Threads() and lp.FindEventBy()
DefaultEventsLimit = 1000
) )
// Linkpearl object // Linkpearl object
@@ -32,6 +34,7 @@ type Linkpearl struct {
joinPermit func(*event.Event) bool joinPermit func(*event.Event) bool
autoleave bool autoleave bool
maxretries int maxretries int
eventsLimit int
} }
type ReqPresence struct { type ReqPresence struct {
@@ -46,6 +49,9 @@ func setDefaults(cfg *Config) {
if cfg.AccountDataCache == 0 { if cfg.AccountDataCache == 0 {
cfg.AccountDataCache = DefaultAccountDataCache cfg.AccountDataCache = DefaultAccountDataCache
} }
if cfg.EventsLimit == 0 {
cfg.EventsLimit = DefaultEventsLimit
}
if cfg.JoinPermit == nil { if cfg.JoinPermit == nil {
// By default, we approve all join requests // By default, we approve all join requests
cfg.JoinPermit = func(*event.Event) bool { return true } cfg.JoinPermit = func(*event.Event) bool { return true }
@@ -84,6 +90,7 @@ func New(cfg *Config) (*Linkpearl, error) {
joinPermit: cfg.JoinPermit, joinPermit: cfg.JoinPermit,
autoleave: cfg.AutoLeave, autoleave: cfg.AutoLeave,
maxretries: cfg.MaxRetries, maxretries: cfg.MaxRetries,
eventsLimit: cfg.EventsLimit,
} }
db, err := dbutil.NewWithDB(cfg.DB, cfg.Dialect) db, err := dbutil.NewWithDB(cfg.DB, cfg.Dialect)

View File

@@ -43,24 +43,37 @@ func (l *Linkpearl) SendNotice(roomID id.RoomID, message string, relates ...*eve
} }
// SendFile to a matrix room // SendFile to a matrix room
func (l *Linkpearl) SendFile(roomID id.RoomID, req *mautrix.ReqUploadMedia, msgtype event.MessageType, relation *event.RelatesTo) error { func (l *Linkpearl) SendFile(roomID id.RoomID, req *mautrix.ReqUploadMedia, msgtype event.MessageType, relates ...*event.RelatesTo) error {
var relation *event.RelatesTo
if len(relates) > 0 {
relation = relates[0]
}
resp, err := l.GetClient().UploadMedia(*req) resp, err := l.GetClient().UploadMedia(*req)
if err != nil { if err != nil {
err = UnwrapError(err) err = UnwrapError(err)
l.log.Error().Err(err).Str("file", req.FileName).Msg("cannot upload file") l.log.Error().Err(err).Str("file", req.FileName).Msg("cannot upload file")
return err return err
} }
_, err = l.Send(roomID, &event.Content{ content := &event.MessageEventContent{
Parsed: &event.MessageEventContent{
MsgType: msgtype, MsgType: msgtype,
Body: req.FileName, Body: req.FileName,
URL: resp.ContentURI.CUString(), URL: resp.ContentURI.CUString(),
RelatesTo: relation, RelatesTo: relation,
}, }
})
_, err = l.Send(roomID, content)
err = UnwrapError(err) err = UnwrapError(err)
if err != nil { if err != nil {
l.log.Error().Err(err).Str("file", req.FileName).Msg("cannot send uploaded file") l.log.Error().Err(err).Str("roomID", roomID.String()).Str("retries", "1/2").Msg("cannot send file into the room")
if relation != nil {
content.RelatesTo = nil
_, err = l.Send(roomID, &content)
err = UnwrapError(err)
if err != nil {
l.log.Error().Err(UnwrapError(err)).Str("roomID", roomID.String()).Str("retries", "2/2").Msg("cannot send file into the room even without relations")
}
}
} }
return err return err

View File

@@ -79,7 +79,7 @@ func (l *Linkpearl) tryJoin(roomID id.RoomID, retry int) {
err = UnwrapError(err) err = UnwrapError(err)
if err != nil { if err != nil {
l.log.Error().Err(err).Str("roomID", roomID.String()).Msg("cannot join room") l.log.Error().Err(err).Str("roomID", roomID.String()).Msg("cannot join room")
if strings.HasPrefix(err.Error(), "403") { // no permission to join, no need to retry if strings.HasPrefix(err.Error(), "403") || strings.HasPrefix(err.Error(), "M_FORBIDDEN") { // no permission to join, no need to retry
return return
} }
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)

View File

@@ -84,11 +84,7 @@ func EventContains[T comparable](evt *event.Event, field string, value T) bool {
if evt.Content.Raw == nil { if evt.Content.Raw == nil {
return false return false
} }
if EventField[T](&evt.Content, field) != value { return EventField[T](&evt.Content, field) == value
return false
}
return true
} }
// EventField returns field value from raw event content // EventField returns field value from raw event content

2
vendor/modules.txt vendored
View File

@@ -144,7 +144,7 @@ gitlab.com/etke.cc/go/trysmtp
# gitlab.com/etke.cc/go/validator v1.0.6 # gitlab.com/etke.cc/go/validator v1.0.6
## explicit; go 1.18 ## explicit; go 1.18
gitlab.com/etke.cc/go/validator gitlab.com/etke.cc/go/validator
# gitlab.com/etke.cc/linkpearl v0.0.0-20230929133006-84554ee97edb # gitlab.com/etke.cc/linkpearl v0.0.0-20231007103859-01907e2b75f2
## explicit; go 1.18 ## explicit; go 1.18
gitlab.com/etke.cc/linkpearl gitlab.com/etke.cc/linkpearl
# go.mau.fi/util v0.1.0 # go.mau.fi/util v0.1.0