feat(chat)!: use terminal notifications (#823)

This commit is contained in:
ayn2op 2026-07-31 21:03:02 -04:00 • committed by GitHub
parent 93a137d982
commit 50850f5e13
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 41 additions and 208 deletions

10
go.mod
View file

@ -11,9 +11,7 @@ require (
github.com/ayn2op/arikawa/v3 v3.0.0-20260708075022-9cc760fe507e
github.com/ayn2op/ningen/v3 v3.0.1-0.20260708075321-0ba1ffc70bd6
github.com/ayn2op/tview v0.0.0-20260730175844-eae642abc285
github.com/deckarep/gosx-notifier v0.0.0-20180201035817-e127226297fb
github.com/gdamore/tcell/v3 v3.4.1
github.com/gen2brain/beeep v0.11.2
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
@ -29,7 +27,6 @@ require (
)
require (
git.sr.ht/~jackmordaunt/go-toast v1.1.2 // indirect
github.com/akavel/rsrc v0.10.2 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
@ -37,20 +34,13 @@ require (
github.com/dchest/jsmin v1.0.0 // indirect
github.com/dlclark/regexp2/v2 v2.5.2 // indirect
github.com/ebitengine/purego v0.10.2 // indirect
github.com/esiqveland/notify v0.14.0 // indirect
github.com/gdamore/encoding v1.0.1 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/godbus/dbus/v5 v5.2.2 // indirect
github.com/gorilla/schema v1.4.1 // indirect
github.com/jackmordaunt/icns/v3 v3.0.1 // indirect
github.com/josephspurrier/goversioninfo v1.7.0 // indirect
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/randall77/makefat v0.0.0-20260406194835-1b91746796b7 // indirect
github.com/sergeymakinen/go-bmp v1.0.0 // indirect
github.com/sergeymakinen/go-ico v1.0.0 // indirect
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
github.com/twmb/murmur3 v1.1.8 // indirect
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
golang.design/x/x11 v0.2.0 // indirect

31
go.sum
View file

@ -1,5 +1,3 @@
git.sr.ht/~jackmordaunt/go-toast v1.1.2 h1:/yrfI55LRt1M7H1vkaw+NaH1+L1CDxrqDltwm5euVuE=
git.sr.ht/~jackmordaunt/go-toast v1.1.2/go.mod h1:jA4OqHKTQ4AFBdwrSnwnskUIIS3HYzlJSgdzCKqfavo=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw=
@ -24,28 +22,18 @@ github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJ
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ=
github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/jsmin v1.0.0 h1:Y2hWXmGZiRxtl+VcTksyucgTlYxnhPzTozCwx9gy9zI=
github.com/dchest/jsmin v1.0.0/go.mod h1:AVBIund7Mr7lKXT70hKT2YgL3XEXUaUk5iw9DZ8b0Uc=
github.com/deckarep/gosx-notifier v0.0.0-20180201035817-e127226297fb h1:6S+TKObz6+Io2c8IOkcbK4Sz7nj6RpEVU7TkvmsZZcw=
github.com/deckarep/gosx-notifier v0.0.0-20180201035817-e127226297fb/go.mod h1:wf3nKtOnQqCp7kp9xB7hHnNlZ6m3NoiOxjrB9hFRq4Y=
github.com/dlclark/regexp2/v2 v2.5.2 h1:HAsucWRhsqcDzl6Ua9aR8JwYOTzrZyPrF0/FNxJVAI0=
github.com/dlclark/regexp2/v2 v2.5.2/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU=
github.com/ebitengine/purego v0.10.2 h1:W809HbnvzAxgdm+aOvlSekrM16wGCdT/e76+9tS7gzE=
github.com/ebitengine/purego v0.10.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/esiqveland/notify v0.14.0 h1:LgIT/vYnziiw3d4ykaeyytc7jBUhD+Nvv3U7GIuPFj4=
github.com/esiqveland/notify v0.14.0/go.mod h1:hesw/IRYTO0x99u1JPweAl4+5mwXJibQVUcP0Iu5ORE=
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
github.com/gdamore/tcell/v3 v3.4.1 h1:22227t1EUwqxTlmCX9vw0RUE2IEPGw6oYcNan+bPe4w=
github.com/gdamore/tcell/v3 v3.4.1/go.mod h1:YWwuxZNi14VGQC5g2VGNEDRXpBraTwvVjMovRH6G6hw=
github.com/gen2brain/beeep v0.11.2 h1:+KfiKQBbQCuhfJFPANZuJ+oxsSKAYNe88hIpJuyKWDA=
github.com/gen2brain/beeep v0.11.2/go.mod h1:jQVvuwnLuwOcdctHn/uyh8horSBNJ8uGb9Cn2W4tvoc=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ=
github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
@ -58,8 +46,6 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/jackmordaunt/icns/v3 v3.0.1 h1:xxot6aNuGrU+lNgxz5I5H0qSeCjNKp8uTXB1j8D4S3o=
github.com/jackmordaunt/icns/v3 v3.0.1/go.mod h1:5sHL59nqTd2ynTnowxB/MDQFhKNqkK8X687uKNygaSQ=
github.com/josephspurrier/goversioninfo v1.7.0 h1:LQzXOlVm/CtbwJ9/UHl5a2HT0BjcLAwid5gqGd7ZUJ8=
github.com/josephspurrier/goversioninfo v1.7.0/go.mod h1:z9y0r2G6g5jwSJaFE0cxW9to0aeIibK7UYeLx53aQRU=
github.com/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk=
@ -70,8 +56,6 @@ github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW
github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/ncruces/zenity v0.10.14 h1:OBFl7qfXcvsdo1NUEGxTlZvAakgWMqz9nG38TuiaGLI=
github.com/ncruces/zenity v0.10.14/go.mod h1:ZBW7uVe/Di3IcRYH0Br8X59pi+O6EPnNIOU66YHpOO4=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -82,26 +66,14 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/sahilm/fuzzy v0.1.3 h1:juByESSS32nVD81vr6tHmKmA/8zde7gE+x5CLxrzXPU=
github.com/sahilm/fuzzy v0.1.3/go.mod h1:au6//VbVSqu6DFrkL2CfjlJ5iURpNCPeE+1GwY3XsT8=
github.com/sergeymakinen/go-bmp v1.0.0 h1:SdGTzp9WvCV0A1V0mBeaS7kQAwNLdVJbmHlqNWq0R+M=
github.com/sergeymakinen/go-bmp v1.0.0/go.mod h1:/mxlAQZRLxSvJFNIEGGLBE/m40f3ZnUifpgVDlcUIEY=
github.com/sergeymakinen/go-ico v1.0.0 h1:uL3khgvKkY6WfAetA+RqsguClBuu7HpvBB/nq/Jvr80=
github.com/sergeymakinen/go-ico v1.0.0/go.mod h1:wQ47mTczswBO5F0NoDt7O0IXgnV4Xy3ojrroMQzyhUk=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk=
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o=
github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
@ -141,7 +113,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
@ -164,7 +135,5 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -29,14 +29,7 @@ type (
}
Notifications struct {
Enabled bool `toml:"enabled"`
Duration int `toml:"duration"`
Sound Sound `toml:"sound"`
}
Sound struct {
Enabled bool `toml:"enabled"`
OnlyOnPing bool `toml:"only_on_ping"`
Enabled bool `toml:"enabled"`
}
TypingIndicator struct {

View file

@ -58,11 +58,6 @@ character = "─"
[notifications]
enabled = true
# The duration of the sound. Set the value to `0` to use default duration. This is only supported on Unix and Windows.
duration = 0
[notifications.sound]
enabled = true
only_on_ping = true
[typing_indicator]
# Whether to send typing status or not.

View file

@ -1,17 +0,0 @@
//go:build !darwin
package notifications
import "github.com/gen2brain/beeep"
func sendDesktopNotification(title string, message string, image string, playSound bool, duration int) error {
if err := beeep.Notify(title, message, image); err != nil {
return err
}
if playSound {
return beeep.Beep(beeep.DefaultFreq, duration)
}
return nil
}

View file

@ -1,19 +0,0 @@
//go:build darwin
package notifications
import (
gosxnotifier "github.com/deckarep/gosx-notifier"
)
func sendDesktopNotification(title string, message string, image string, playSound bool, _ int) error {
notification := gosxnotifier.NewNotification(message)
notification.Title = title
notification.ContentImage = image
if playSound {
notification.Sound = gosxnotifier.Default
}
return notification.Push()
}

View file

@ -1,114 +0,0 @@
package notifications
import (
"fmt"
"io"
"log/slog"
"net/http"
"os"
"path/filepath"
"github.com/ayn2op/arikawa/v3/discord"
"github.com/ayn2op/arikawa/v3/gateway"
"github.com/ayn2op/discordo/internal/config"
"github.com/ayn2op/discordo/internal/consts"
"github.com/ayn2op/ningen/v3"
)
func Notify(state *ningen.State, message gateway.MessageCreateEvent, cfg *config.Config) error {
if !cfg.Notifications.Enabled || cfg.Status == discord.DoNotDisturbStatus {
return nil
}
mentions := state.MessageMentions(&message.Message)
if mentions == 0 {
return nil
}
// Handle sent files
content := message.Content
if message.Content == "" && len(message.Attachments) > 0 {
content = "Uploaded " + message.Attachments[0].Filename
}
if content == "" {
return nil
}
title := message.Author.DisplayOrUsername()
channel, err := state.Cabinet.Channel(message.ChannelID)
if err != nil {
return fmt.Errorf("failed to get channel from state: %w", err)
}
if channel.GuildID.IsValid() {
guild, err := state.Cabinet.Guild(channel.GuildID)
if err != nil {
return fmt.Errorf("failed to get guild from state: %w", err)
}
if member := message.Member; member != nil && member.Nick != "" {
title = member.Nick
}
title += " (#" + channel.Name + ", " + guild.Name + ")"
}
hash := message.Author.Avatar
if hash == "" {
hash = "default"
}
imagePath, err := getCachedProfileImage(hash, message.Author.AvatarURLWithType(discord.PNGImage))
if err != nil {
slog.Info("failed to get profile image from cache for notification", "err", err, "hash", hash)
}
shouldChime := cfg.Notifications.Sound.Enabled && (!cfg.Notifications.Sound.OnlyOnPing || mentions.Has(ningen.MessageMentions|ningen.MessageNotifies))
if err := sendDesktopNotification(title, content, imagePath, shouldChime, cfg.Notifications.Duration); err != nil {
return err
}
return nil
}
func getCachedProfileImage(avatarHash discord.Hash, url string) (string, error) {
path := filepath.Join(consts.CacheDir(), "avatars")
if err := os.MkdirAll(path, os.ModePerm); err != nil {
return "", err
}
root, err := os.OpenRoot(path)
if err != nil {
return "", fmt.Errorf("failed to open avatars dir: %w", err)
}
defer root.Close()
filename := avatarHash + ".png"
_, err = root.Stat(filename)
switch {
case err == nil:
return filepath.Join(path, filename), nil
case os.IsNotExist(err):
file, err := root.Create(filename)
if err != nil {
return "", err
}
defer file.Close()
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
if _, err := io.Copy(file, resp.Body); err != nil {
return "", err
}
return file.Name(), nil
default:
return "", err
}
}

View file

@ -8,7 +8,6 @@ import (
"github.com/ayn2op/arikawa/v3/gateway"
"github.com/ayn2op/arikawa/v3/utils/httputil/httpdriver"
"github.com/ayn2op/arikawa/v3/utils/ws"
"github.com/ayn2op/discordo/internal/notifications"
"github.com/ayn2op/ningen/v3"
"github.com/ayn2op/ningen/v3/states/read"
"github.com/ayn2op/tview"
@ -108,10 +107,47 @@ func (m *Model) onMessageCreate(message *gateway.MessageCreateEvent) tview.Cmd {
func (m *Model) notify(message gateway.MessageCreateEvent) tview.Cmd {
return func() tview.Msg {
if err := notifications.Notify(m.state, message, m.cfg); err != nil {
slog.Error("failed to notify", "err", err, "channel_id", message.ChannelID, "message_id", message.ID)
if !m.cfg.Notifications.Enabled || m.cfg.Status == discord.DoNotDisturbStatus {
return nil
}
return nil
mentions := m.state.MessageMentions(&message.Message)
if mentions == 0 {
return nil
}
// Handle sent files
content := message.Content
if message.Content == "" && len(message.Attachments) > 0 {
content = "Uploaded " + message.Attachments[0].Filename
}
if content == "" {
return nil
}
title := message.Author.DisplayOrUsername()
channel, err := m.state.Cabinet.Channel(message.ChannelID)
if err != nil {
slog.Error("failed to get channel from state", "err", err, "channel_id", message.ChannelID)
return nil
}
if channel.GuildID.IsValid() {
guild, err := m.state.Cabinet.Guild(channel.GuildID)
if err != nil {
slog.Error("failed to get guild from state", "err", err, "guild_id", channel.GuildID)
return nil
}
if member := message.Member; member != nil && member.Nick != "" {
title = member.Nick
}
title += " (#" + channel.Name + ", " + guild.Name + ")"
}
return tview.Notify(title, content)()
}
}