Merge branch 'main' into 'user-whitelisting'
# Conflicts: # bot/bot.go
This commit is contained in:
39
README.md
39
README.md
@@ -25,15 +25,14 @@ An Email to Matrix bridge
|
|||||||
|
|
||||||
env vars
|
env vars
|
||||||
|
|
||||||
### mandatory
|
|
||||||
|
|
||||||
* **POSTMOOGLE_HOMESERVER** - homeserver url, eg: `https://matrix.example.com`
|
* **POSTMOOGLE_HOMESERVER** - homeserver url, eg: `https://matrix.example.com`
|
||||||
* **POSTMOOGLE_LOGIN** - user login/localpart, eg: `moogle`
|
* **POSTMOOGLE_LOGIN** - user login/localpart, eg: `moogle`
|
||||||
* **POSTMOOGLE_PASSWORD** - user password
|
* **POSTMOOGLE_PASSWORD** - user password
|
||||||
* **POSTMOOGLE_DOMAIN** - SMTP domain to listen for new emails
|
* **POSTMOOGLE_DOMAIN** - SMTP domain to listen for new emails
|
||||||
* **POSTMOOGLE_PORT** - SMTP port to listen for new emails
|
* **POSTMOOGLE_PORT** - SMTP port to listen for new emails
|
||||||
|
|
||||||
### optional
|
<details>
|
||||||
|
<summary>other optional config parameters</summary>
|
||||||
|
|
||||||
* **POSTMOOGLE_NOOWNER** - allow change room settings by any room partisipant
|
* **POSTMOOGLE_NOOWNER** - allow change room settings by any room partisipant
|
||||||
* **POSTMOOGLE_FEDERATION** - allow usage of Postmoogle by users from others homeservers
|
* **POSTMOOGLE_FEDERATION** - allow usage of Postmoogle by users from others homeservers
|
||||||
@@ -48,6 +47,40 @@ env vars
|
|||||||
|
|
||||||
You can find default values in [config/defaults.go](config/defaults.go)
|
You can find default values in [config/defaults.go](config/defaults.go)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### How to start
|
||||||
|
|
||||||
|
1. Invite the bot into a room you want to use as mailbox
|
||||||
|
2. Read the bot's introduction
|
||||||
|
3. Set mailbox using `!pm mailbox NAME` where `NAME` is part of email (e.g. `NAME@example.com`)
|
||||||
|
4. Done. Mailbox owner and other options will be set automatically when you configure mailbox.
|
||||||
|
If you want to change them - check available options in the help message (`!pm help`)
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Full list of available commands</summary>
|
||||||
|
|
||||||
|
* **!pm help** - Show help message
|
||||||
|
* **!pm stop** - Disable bridge for the room and clear all configuration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
* **!pm mailbox** - Get or set mailbox of the room
|
||||||
|
* **!pm owner** - Get or set owner of the room
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
* **!pm nosender** - Get or set `nosender` of the room (`true` - hide email sender; `false` - show email sender)
|
||||||
|
* **!pm nosubject** - Get or set `nosubject` of the room (`true` - hide email subject; `false` - show email subject)
|
||||||
|
* **!pm nohtml** - Get or set `nohtml` of the room (`true` - ignore HTML in email; `false` - parse HTML in emails)
|
||||||
|
* **!pm nothreads** - Get or set `nothreads` of the room (`true` - ignore email threads; `false` - convert email threads into matrix threads)
|
||||||
|
* **!pm nofiles** - Get or set `nofiles` of the room (`true` - ignore email attachments; `false` - upload email attachments)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
## Where to get
|
## Where to get
|
||||||
|
|
||||||
[docker registry](https://gitlab.com/etke.cc/postmoogle/container_registry), [etke.cc](https://etke.cc)
|
[docker registry](https://gitlab.com/etke.cc/postmoogle/container_registry), [etke.cc](https://etke.cc)
|
||||||
|
|||||||
30
bot/bot.go
30
bot/bot.go
@@ -24,20 +24,22 @@ type Bot struct {
|
|||||||
rooms sync.Map
|
rooms sync.Map
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
lp *linkpearl.Linkpearl
|
lp *linkpearl.Linkpearl
|
||||||
|
mu map[id.RoomID]*sync.Mutex
|
||||||
handledMembershipEvents sync.Map
|
handledMembershipEvents sync.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new matrix bot
|
// New creates a new matrix bot
|
||||||
func New(lp *linkpearl.Linkpearl, log *logger.Logger, prefix, domain string, noowner, federation bool, allowedUsers []*regexp.Regexp) *Bot {
|
func New(lp *linkpearl.Linkpearl, log *logger.Logger, prefix, domain string, noowner, federation bool, allowedUsers []*regexp.Regexp) *Bot {
|
||||||
return &Bot{
|
return &Bot{
|
||||||
noowner: noowner,
|
noowner: noowner,
|
||||||
federation: federation,
|
federation: federation,
|
||||||
prefix: prefix,
|
prefix: prefix,
|
||||||
domain: domain,
|
domain: domain,
|
||||||
allowedUsers: allowedUsers,
|
allowedUsers: allowedUsers,
|
||||||
rooms: sync.Map{},
|
rooms: sync.Map{},
|
||||||
log: log,
|
log: log,
|
||||||
lp: lp,
|
lp: lp,
|
||||||
|
mu: map[id.RoomID]*sync.Mutex{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,20 +81,6 @@ func (b *Bot) Start(statusMsg string) error {
|
|||||||
return b.lp.Start(statusMsg)
|
return b.lp.Start(statusMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMappings returns mapping of mailbox = room
|
|
||||||
func (b *Bot) GetMapping(mailbox string) (id.RoomID, bool) {
|
|
||||||
v, ok := b.rooms.Load(mailbox)
|
|
||||||
if !ok {
|
|
||||||
return "", ok
|
|
||||||
}
|
|
||||||
roomID, ok := v.(id.RoomID)
|
|
||||||
if !ok {
|
|
||||||
return "", ok
|
|
||||||
}
|
|
||||||
|
|
||||||
return roomID, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop the bot
|
// Stop the bot
|
||||||
func (b *Bot) Stop() {
|
func (b *Bot) Stop() {
|
||||||
err := b.lp.GetClient().SetPresence(event.PresenceOffline)
|
err := b.lp.GetClient().SetPresence(event.PresenceOffline)
|
||||||
|
|||||||
@@ -256,11 +256,16 @@ func (b *Bot) setOption(ctx context.Context, name, value string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
old := cfg.Get(name)
|
||||||
cfg.Set(name, value)
|
cfg.Set(name, value)
|
||||||
|
|
||||||
if name == optionMailbox {
|
if name == optionMailbox {
|
||||||
value = fmt.Sprintf("%s@%s", value, b.domain)
|
|
||||||
cfg.Set(optionOwner, evt.Sender.String())
|
cfg.Set(optionOwner, evt.Sender.String())
|
||||||
|
if old != "" {
|
||||||
|
b.rooms.Delete(old)
|
||||||
|
}
|
||||||
b.rooms.Store(value, evt.RoomID)
|
b.rooms.Store(value, evt.RoomID)
|
||||||
|
value = fmt.Sprintf("%s@%s", value, b.domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = b.setSettings(evt.RoomID, cfg)
|
err = b.setSettings(evt.RoomID, cfg)
|
||||||
|
|||||||
18
bot/email.go
18
bot/email.go
@@ -43,12 +43,28 @@ func email2content(email *utils.Email, cfg settings, threadID id.EventID) *event
|
|||||||
return &content
|
return &content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMapping returns mapping of mailbox = room
|
||||||
|
func (b *Bot) GetMapping(mailbox string) (id.RoomID, bool) {
|
||||||
|
v, ok := b.rooms.Load(mailbox)
|
||||||
|
if !ok {
|
||||||
|
return "", ok
|
||||||
|
}
|
||||||
|
roomID, ok := v.(id.RoomID)
|
||||||
|
if !ok {
|
||||||
|
return "", ok
|
||||||
|
}
|
||||||
|
|
||||||
|
return roomID, ok
|
||||||
|
}
|
||||||
|
|
||||||
// Send email to matrix room
|
// Send email to matrix room
|
||||||
func (b *Bot) Send(ctx context.Context, email *utils.Email) error {
|
func (b *Bot) Send(ctx context.Context, email *utils.Email) error {
|
||||||
roomID, ok := b.GetMapping(utils.Mailbox(email.To))
|
roomID, ok := b.GetMapping(utils.Mailbox(email.To))
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("room not found")
|
return errors.New("room not found")
|
||||||
}
|
}
|
||||||
|
b.lock(roomID)
|
||||||
|
defer b.unlock(roomID)
|
||||||
|
|
||||||
cfg, err := b.getSettings(roomID)
|
cfg, err := b.getSettings(roomID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -66,7 +82,7 @@ func (b *Bot) Send(ctx context.Context, email *utils.Email) error {
|
|||||||
content := email2content(email, cfg, threadID)
|
content := email2content(email, cfg, threadID)
|
||||||
eventID, serr := b.lp.Send(roomID, content)
|
eventID, serr := b.lp.Send(roomID, content)
|
||||||
if serr != nil {
|
if serr != nil {
|
||||||
return serr
|
return utils.UnwrapError(serr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if threadID == "" && !cfg.NoThreads() {
|
if threadID == "" && !cfg.NoThreads() {
|
||||||
|
|||||||
26
bot/mutext.go
Normal file
26
bot/mutext.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package bot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"maunium.net/go/mautrix/id"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *Bot) lock(roomID id.RoomID) {
|
||||||
|
_, ok := b.mu[roomID]
|
||||||
|
if !ok {
|
||||||
|
b.mu[roomID] = &sync.Mutex{}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.mu[roomID].Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) unlock(roomID id.RoomID) {
|
||||||
|
_, ok := b.mu[roomID]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.mu[roomID].Unlock()
|
||||||
|
delete(b.mu, roomID)
|
||||||
|
}
|
||||||
@@ -117,9 +117,9 @@ func (b *Bot) getSettings(roomID id.RoomID) (settings, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, err
|
return config, utils.UnwrapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) setSettings(roomID id.RoomID, cfg settings) error {
|
func (b *Bot) setSettings(roomID id.RoomID, cfg settings) error {
|
||||||
return b.lp.GetClient().SetRoomAccountData(roomID, settingskey, cfg)
|
return utils.UnwrapError(b.lp.GetClient().SetRoomAccountData(roomID, settingskey, cfg))
|
||||||
}
|
}
|
||||||
|
|||||||
8
e2e/send-stress
Executable file
8
e2e/send-stress
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
for i in {0..10..1}; do
|
||||||
|
echo "#${i}..."
|
||||||
|
ssmtp test@localhost < $1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "done"
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"maunium.net/go/mautrix"
|
||||||
"maunium.net/go/mautrix/event"
|
"maunium.net/go/mautrix/event"
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
)
|
)
|
||||||
@@ -24,3 +25,29 @@ func RelatesTo(noThreads bool, parentID id.EventID) *event.RelatesTo {
|
|||||||
EventID: parentID,
|
EventID: parentID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnwrapError tries to unwrap a error into something meaningful, like mautrix.HTTPError or mautrix.RespError
|
||||||
|
func UnwrapError(err error) error {
|
||||||
|
switch err.(type) {
|
||||||
|
case nil:
|
||||||
|
return nil
|
||||||
|
case mautrix.HTTPError:
|
||||||
|
return unwrapHTTPError(err)
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unwrapHTTPError(err error) error {
|
||||||
|
httperr, ok := err.(mautrix.HTTPError)
|
||||||
|
if !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
uwerr := httperr.Unwrap()
|
||||||
|
if uwerr != nil {
|
||||||
|
return uwerr
|
||||||
|
}
|
||||||
|
|
||||||
|
return httperr
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user