diff --git a/go.mod b/go.mod index 19cc1ad..0c88856 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/diamondburned/cchat-discord go 1.14 require ( - github.com/diamondburned/arikawa v1.3.6 + github.com/diamondburned/arikawa/v2 v2.0.0-20201220032235-088b30430377 github.com/diamondburned/cchat v0.3.15 - github.com/diamondburned/ningen v0.2.1-0.20201023061015-ce64ffb0bb12 + github.com/diamondburned/ningen/v2 v2.0.0-20201220054153-c69c4f7057b4 github.com/dustin/go-humanize v1.0.0 github.com/go-test/deep v1.0.7 github.com/lithammer/fuzzysearch v1.1.1 diff --git a/go.sum b/go.sum index 849915d..c6acbc2 100644 --- a/go.sum +++ b/go.sum @@ -42,9 +42,25 @@ github.com/diamondburned/arikawa v1.2.0 h1:3dFmpk/G4UwO+Kto0tXd5AbaCKC9KH2ZfnA8U github.com/diamondburned/arikawa v1.2.0/go.mod h1:nIhVIatzTQhPUa7NB8w4koG1RF9gYbpAr8Fj8sKq660= github.com/diamondburned/arikawa v1.3.0 h1:up5q5Ya/QbiFqhMejvl+c03YdsgzkzspsJOWW30A2lk= github.com/diamondburned/arikawa v1.3.0/go.mod h1:nIhVIatzTQhPUa7NB8w4koG1RF9gYbpAr8Fj8sKq660= +github.com/diamondburned/arikawa v1.3.2 h1:ftWgP95IJGXNvCvtO5x0QBYsnFSnIBY0SvDdGoC3ILA= github.com/diamondburned/arikawa v1.3.2/go.mod h1:nIhVIatzTQhPUa7NB8w4koG1RF9gYbpAr8Fj8sKq660= github.com/diamondburned/arikawa v1.3.6 h1:DhxWDO4fyXAZ4VFrWdJOqiiJKJRpkrehGJwPtZ2eos0= github.com/diamondburned/arikawa v1.3.6/go.mod h1:nIhVIatzTQhPUa7NB8w4koG1RF9gYbpAr8Fj8sKq660= +github.com/diamondburned/arikawa/v2 v2.0.0-20201208195035-9911a3d6627a/go.mod h1:e+lhS20ni2luFEU06Pc8paCxgZL99/RZb77dOC82CF0= +github.com/diamondburned/arikawa/v2 v2.0.0-20201218230021-9df396bb7f14 h1:2+j49uXPC44gEFstt0hlY17Wz88HU7DXgOmQmgIf28E= +github.com/diamondburned/arikawa/v2 v2.0.0-20201218230021-9df396bb7f14/go.mod h1:e+lhS20ni2luFEU06Pc8paCxgZL99/RZb77dOC82CF0= +github.com/diamondburned/arikawa/v2 v2.0.0-20201219061804-88911a7d1117 h1:n769tNSjYU2EDCkM3PBLTaB0LQwN9Sr3Rab9KXVxhoc= +github.com/diamondburned/arikawa/v2 v2.0.0-20201219061804-88911a7d1117/go.mod h1:e+lhS20ni2luFEU06Pc8paCxgZL99/RZb77dOC82CF0= +github.com/diamondburned/arikawa/v2 v2.0.0-20201219063311-c9dd51aeb654 h1:z4/LcUUQ/B7l3+G1fh16/ByW206/oueKLANdygetEyA= +github.com/diamondburned/arikawa/v2 v2.0.0-20201219063311-c9dd51aeb654/go.mod h1:e+lhS20ni2luFEU06Pc8paCxgZL99/RZb77dOC82CF0= +github.com/diamondburned/arikawa/v2 v2.0.0-20201219065126-f11edb7260b2 h1:73ukkYNYZztR3wUXzeyza6AG4zBZjA8ER6qO0QGZPPA= +github.com/diamondburned/arikawa/v2 v2.0.0-20201219065126-f11edb7260b2/go.mod h1:e+lhS20ni2luFEU06Pc8paCxgZL99/RZb77dOC82CF0= +github.com/diamondburned/arikawa/v2 v2.0.0-20201219075756-36c2f166becd h1:HCaw0YrlQWKTao7QE6s8tjYImZEpH7Lad85bnYel1V0= +github.com/diamondburned/arikawa/v2 v2.0.0-20201219075756-36c2f166becd/go.mod h1:/vapSS3yfYRAt5hhgI6JiPkca+wKhgi0MdanT1dBBQY= +github.com/diamondburned/arikawa/v2 v2.0.0-20201220000828-3e2814748f9a h1:aA8K3+JfXdJPcK9Ms4WmHLGp+qnKT0VuNWOUZUP9YpY= +github.com/diamondburned/arikawa/v2 v2.0.0-20201220000828-3e2814748f9a/go.mod h1:e+lhS20ni2luFEU06Pc8paCxgZL99/RZb77dOC82CF0= +github.com/diamondburned/arikawa/v2 v2.0.0-20201220032235-088b30430377 h1:71BLnECSl0/Ns7iZmEm7MpE5+qSuWw/BQBQY2XCUmVc= +github.com/diamondburned/arikawa/v2 v2.0.0-20201220032235-088b30430377/go.mod h1:e+lhS20ni2luFEU06Pc8paCxgZL99/RZb77dOC82CF0= github.com/diamondburned/cchat v0.0.34 h1:BGiVxMRA9dmW3rLilIldBvjVan7eTTpaWCCfX9IKBYU= github.com/diamondburned/cchat v0.0.34/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU= github.com/diamondburned/cchat v0.0.35 h1:WiMGl8BQJgbP9E4xRxgLGlqUsHpTcJgDKDt8/6a7lBk= @@ -163,6 +179,24 @@ github.com/diamondburned/ningen v0.1.1-0.20200820222640-35796f938a58 h1:sQvq5Dgm github.com/diamondburned/ningen v0.1.1-0.20200820222640-35796f938a58/go.mod h1:aXm69MB+Qu04OjBiixQw28zRijDc49vruMJMaHZ2c0Q= github.com/diamondburned/ningen v0.2.1-0.20201023061015-ce64ffb0bb12 h1:hcabAq2jPmEhrfSRrl2hHkpQXtPTIh7L58fvCxBO/C4= github.com/diamondburned/ningen v0.2.1-0.20201023061015-ce64ffb0bb12/go.mod h1:TcvJV0bK4bp7t+7m29/Tz9dCqgA0sJBKM/Igt0WkvT4= +github.com/diamondburned/ningen v1.0.0 h1:fr+7oDWA0Db73CuVeLY8SScWdW6ft/aWwkULXD0flKw= +github.com/diamondburned/ningen v1.0.0/go.mod h1:TcvJV0bK4bp7t+7m29/Tz9dCqgA0sJBKM/Igt0WkvT4= +github.com/diamondburned/ningen/v2 v2.0.0-20201210080809-a14408097980 h1:g/ndgdW9sdL2KEIORhbRafnwreDksZVTPQtRF3SgriU= +github.com/diamondburned/ningen/v2 v2.0.0-20201210080809-a14408097980/go.mod h1:ub/rJpU3cD1Izpa1g+JDuzmtbfP/grV2ZSK5GzL94nc= +github.com/diamondburned/ningen/v2 v2.0.0-20201219070301-15610044db9a h1:w8CWPYiwH9p2XGlHHeTqRWx7e8CJJLN8i4orAkOa27Y= +github.com/diamondburned/ningen/v2 v2.0.0-20201219070301-15610044db9a/go.mod h1:Pw4ZPQmZUonCytlKhHgan98CZeCQ4AWh0DWqvnhsuNE= +github.com/diamondburned/ningen/v2 v2.0.0-20201219225720-58beeea47f6d h1:2DWG8srhOrOJDffCk37L66p5yLJssY5wGdt7OPd7Y68= +github.com/diamondburned/ningen/v2 v2.0.0-20201219225720-58beeea47f6d/go.mod h1:9ZWVTlDzwR0/tGFrBF6iiQV8qPXlJSKsIqRIRyXtDDU= +github.com/diamondburned/ningen/v2 v2.0.0-20201219233304-7023c9c95229 h1:Or3Qy+gwKeIECMv2S9rWsF3xRy90kpHKgCgmixG7ybE= +github.com/diamondburned/ningen/v2 v2.0.0-20201219233304-7023c9c95229/go.mod h1:9ZWVTlDzwR0/tGFrBF6iiQV8qPXlJSKsIqRIRyXtDDU= +github.com/diamondburned/ningen/v2 v2.0.0-20201219234158-543154247f29 h1:rwyTYHlPHSO8dAAqgeSIaXLQLPlX1NC8Ebd1py1JTVg= +github.com/diamondburned/ningen/v2 v2.0.0-20201219234158-543154247f29/go.mod h1:9ZWVTlDzwR0/tGFrBF6iiQV8qPXlJSKsIqRIRyXtDDU= +github.com/diamondburned/ningen/v2 v2.0.0-20201219235002-2c75ebf8f5fc h1:cV3WgIGTNeh8MFqRRgcxjgjTLBrU/1h24jVEeA2qySg= +github.com/diamondburned/ningen/v2 v2.0.0-20201219235002-2c75ebf8f5fc/go.mod h1:9ZWVTlDzwR0/tGFrBF6iiQV8qPXlJSKsIqRIRyXtDDU= +github.com/diamondburned/ningen/v2 v2.0.0-20201220032656-1096439b9379 h1:gcmMWHBtgJD+HVDQqSoy1C5L4g1CVSYadwrdeYgVL30= +github.com/diamondburned/ningen/v2 v2.0.0-20201220032656-1096439b9379/go.mod h1:2ZjyeHqO9jCdlfmJhhVhk8eCumx418n39uVaC/LgEgY= +github.com/diamondburned/ningen/v2 v2.0.0-20201220054153-c69c4f7057b4 h1:qzh5ghfgvUllilOhkrGP29IGQT6DGfcc3lhk9uSA6nU= +github.com/diamondburned/ningen/v2 v2.0.0-20201220054153-c69c4f7057b4/go.mod h1:2ZjyeHqO9jCdlfmJhhVhk8eCumx418n39uVaC/LgEgY= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -199,6 +233,8 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY= github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= +github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= +github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -206,6 +242,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kawasin73/umutex v0.2.1 h1:Onkzz3LKs1HThskVwdhhBocqdRQqwCZ03quDJzuPzPo= +github.com/kawasin73/umutex v0.2.1/go.mod h1:A02N2muKVFMvFlp5c+hBycgdH964YtieGs+7mYB16NU= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -237,6 +275,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -300,6 +339,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -309,6 +349,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/internal/discord/authenticate/login.go b/internal/discord/authenticate/login.go index e4220eb..6d51da7 100644 --- a/internal/discord/authenticate/login.go +++ b/internal/discord/authenticate/login.go @@ -1,7 +1,7 @@ package authenticate import ( - "github.com/diamondburned/arikawa/api" + "github.com/diamondburned/arikawa/v2/api" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/session" "github.com/diamondburned/cchat-discord/internal/discord/state" diff --git a/internal/discord/authenticate/totp.go b/internal/discord/authenticate/totp.go index 614088a..d898885 100644 --- a/internal/discord/authenticate/totp.go +++ b/internal/discord/authenticate/totp.go @@ -1,7 +1,7 @@ package authenticate import ( - "github.com/diamondburned/arikawa/api" + "github.com/diamondburned/arikawa/v2/api" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/session" "github.com/diamondburned/cchat-discord/internal/discord/state" diff --git a/internal/discord/category/category.go b/internal/discord/category/category.go index 7ebe1e3..0f7fa21 100644 --- a/internal/discord/category/category.go +++ b/internal/discord/category/category.go @@ -3,7 +3,7 @@ package category import ( "sort" - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel" "github.com/diamondburned/cchat-discord/internal/discord/state" diff --git a/internal/discord/channel/channel.go b/internal/discord/channel/channel.go index 97ee772..9537b69 100644 --- a/internal/discord/channel/channel.go +++ b/internal/discord/channel/channel.go @@ -1,11 +1,15 @@ package channel import ( - "github.com/diamondburned/arikawa/discord" + "context" + + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/gateway" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/message" "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" "github.com/diamondburned/cchat-discord/internal/discord/state" + "github.com/diamondburned/cchat-discord/internal/urlutils" "github.com/diamondburned/cchat/text" "github.com/diamondburned/cchat/utils/empty" "github.com/pkg/errors" @@ -29,9 +33,11 @@ func New(s *state.Instance, ch discord.Channel) (cchat.Server, error) { func NewChannel(s *state.Instance, ch discord.Channel) (Channel, error) { // Ensure the state keeps the channel's permission. - _, err := s.Permissions(ch.ID, s.UserID) - if err != nil { - return Channel{}, errors.Wrap(err, "Failed to get permission") + if ch.GuildID.IsValid() { + _, err := s.Permissions(ch.ID, s.UserID) + if err != nil { + return Channel{}, errors.Wrap(err, "failed to get permission") + } } sharedCh := shared.Channel{ @@ -56,11 +62,7 @@ func (ch Channel) Name() text.Rich { return text.Rich{Content: ch.ID()} } - if c.NSFW { - return text.Rich{Content: "#" + c.Name + " (nsfw)"} - } else { - return text.Rich{Content: "#" + c.Name} - } + return text.Plain(shared.ChannelName(*c)) } func (ch Channel) AsCommander() cchat.Commander { @@ -74,3 +76,52 @@ func (ch Channel) AsMessenger() cchat.Messenger { return message.New(ch.Channel) } + +func (ch Channel) AsIconer() cchat.Iconer { + // Guild channels never have an icon. + if ch.GuildID.IsValid() { + return nil + } + + c, err := ch.Self() + if err != nil { + return nil + } + + // Only DM channels should have an icon. + if c.Type != discord.DirectMessage { + return nil + } + + return PresenceAvatar{ + user: c.DMRecipients[0], + guild: ch.GuildID, + state: ch.State, + } +} + +type PresenceAvatar struct { + user discord.User + guild discord.GuildID + state *state.Instance +} + +func (avy PresenceAvatar) Icon(ctx context.Context, iconer cchat.IconContainer) (func(), error) { + if avy.user.Avatar != "" { + iconer.SetIcon(urlutils.AvatarURL(avy.user.AvatarURL())) + } + + // There are so many other places that could be checked, but this is good + // enough. + + c, err := avy.state.Presence(avy.guild, avy.user.ID) + if err == nil && c.User.Avatar != "" { + iconer.SetIcon(urlutils.AvatarURL(c.User.AvatarURL())) + } + + return avy.state.AddHandler(func(update *gateway.PresenceUpdateEvent) { + if avy.user.ID == update.User.ID { + iconer.SetIcon(urlutils.AvatarURL(update.User.AvatarURL())) + } + }), nil +} diff --git a/internal/discord/channel/commands/commands.go b/internal/discord/channel/commands/commands.go index 6ff9cbd..468e9b8 100644 --- a/internal/discord/channel/commands/commands.go +++ b/internal/discord/channel/commands/commands.go @@ -8,8 +8,8 @@ import ( "io/ioutil" "strings" - "github.com/diamondburned/arikawa/bot/extras/arguments" - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/bot/extras/arguments" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" "github.com/pkg/errors" ) diff --git a/internal/discord/channel/message/action/actioner.go b/internal/discord/channel/message/action/actioner.go index b8a2be9..c93d183 100644 --- a/internal/discord/channel/message/action/actioner.go +++ b/internal/discord/channel/message/action/actioner.go @@ -1,7 +1,7 @@ package action import ( - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" "github.com/pkg/errors" @@ -43,13 +43,13 @@ func (ac Actioner) Actions(id string) []string { return nil } - m, err := ac.State.Store.Message(ac.ID, discord.MessageID(s)) + m, err := ac.State.Cabinet.Message(ac.ID, discord.MessageID(s)) if err != nil { return nil } // Get the current user. - u, err := ac.State.Store.Me() + u, err := ac.State.Cabinet.Me() if err != nil { return nil } @@ -91,7 +91,7 @@ func (ac Actioner) canManageMessages(userID discord.UserID) bool { return false } - m, err := ac.State.Store.Member(ac.GuildID, userID) + m, err := ac.State.Cabinet.Member(ac.GuildID, userID) if err != nil { return false } diff --git a/internal/discord/channel/message/backlog/backlogger.go b/internal/discord/channel/message/backlog/backlogger.go index 96225a2..5517c71 100644 --- a/internal/discord/channel/message/backlog/backlogger.go +++ b/internal/discord/channel/message/backlog/backlogger.go @@ -3,7 +3,7 @@ package backlog import ( "context" - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" "github.com/diamondburned/cchat-discord/internal/discord/message" diff --git a/internal/discord/channel/message/edit/editor.go b/internal/discord/channel/message/edit/editor.go index d12e60c..3684eb8 100644 --- a/internal/discord/channel/message/edit/editor.go +++ b/internal/discord/channel/message/edit/editor.go @@ -1,7 +1,7 @@ package edit import ( - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" "github.com/pkg/errors" @@ -23,7 +23,7 @@ func (ed Editor) IsEditable(id string) bool { return false } - m, err := ed.State.Store.Message(ed.ID, discord.MessageID(s)) + m, err := ed.State.Cabinet.Message(ed.ID, discord.MessageID(s)) if err != nil { return false } @@ -38,7 +38,7 @@ func (ed Editor) RawContent(id string) (string, error) { return "", errors.Wrap(err, "Failed to parse ID") } - m, err := ed.State.Store.Message(ed.ID, discord.MessageID(s)) + m, err := ed.State.Cabinet.Message(ed.ID, discord.MessageID(s)) if err != nil { return "", errors.Wrap(err, "Failed to get the message") } diff --git a/internal/discord/channel/message/indicate/typing.go b/internal/discord/channel/message/indicate/typing.go index e293799..22331ce 100644 --- a/internal/discord/channel/message/indicate/typing.go +++ b/internal/discord/channel/message/indicate/typing.go @@ -3,7 +3,7 @@ package indicate import ( "time" - "github.com/diamondburned/arikawa/gateway" + "github.com/diamondburned/arikawa/v2/gateway" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" "github.com/diamondburned/cchat-discord/internal/discord/channel/typer" diff --git a/internal/discord/channel/message/indicate/unread.go b/internal/discord/channel/message/indicate/unread.go index 745f353..11baa32 100644 --- a/internal/discord/channel/message/indicate/unread.go +++ b/internal/discord/channel/message/indicate/unread.go @@ -3,7 +3,7 @@ package indicate import ( "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" - "github.com/diamondburned/ningen/states/read" + "github.com/diamondburned/ningen/v2/states/read" "github.com/pkg/errors" ) diff --git a/internal/discord/channel/message/memberlist/member.go b/internal/discord/channel/message/memberlist/member.go index 361479a..6a8cbae 100644 --- a/internal/discord/channel/message/memberlist/member.go +++ b/internal/discord/channel/message/memberlist/member.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - "github.com/diamondburned/arikawa/discord" - "github.com/diamondburned/arikawa/gateway" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/gateway" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" "github.com/diamondburned/cchat-discord/internal/segments/colored" @@ -36,12 +36,12 @@ func (l *Member) ID() cchat.ID { } func (l *Member) Name() text.Rich { - g, err := l.channel.State.Store.Guild(l.channel.GuildID) + g, err := l.channel.State.Cabinet.Guild(l.channel.GuildID) if err != nil { return text.Plain(l.origName) } - m, err := l.channel.State.Store.Member(l.channel.GuildID, l.userID) + m, err := l.channel.State.Cabinet.Member(l.channel.GuildID, l.userID) if err != nil { return text.Plain(l.origName) } @@ -80,19 +80,19 @@ func (l *Member) Icon(ctx context.Context, c cchat.IconContainer) (func(), error } func (l *Member) Status() cchat.Status { - p, err := l.channel.State.Store.Presence(l.channel.GuildID, l.userID) + p, err := l.channel.State.Cabinet.Presence(l.channel.GuildID, l.userID) if err != nil { return cchat.StatusUnknown } switch p.Status { - case discord.OnlineStatus: + case gateway.OnlineStatus: return cchat.StatusOnline - case discord.DoNotDisturbStatus: + case gateway.DoNotDisturbStatus: return cchat.StatusBusy - case discord.IdleStatus: + case gateway.IdleStatus: return cchat.StatusAway - case discord.OfflineStatus, discord.InvisibleStatus: + case gateway.OfflineStatus, gateway.InvisibleStatus: return cchat.StatusOffline default: return cchat.StatusUnknown @@ -100,15 +100,11 @@ func (l *Member) Status() cchat.Status { } func (l *Member) Secondary() text.Rich { - p, err := l.channel.State.Store.Presence(l.channel.GuildID, l.userID) + p, err := l.channel.State.Cabinet.Presence(l.channel.GuildID, l.userID) if err != nil { return text.Plain("") } - if p.Game != nil { - return formatSmallActivity(*p.Game) - } - if len(p.Activities) > 0 { return formatSmallActivity(p.Activities[0]) } diff --git a/internal/discord/channel/message/memberlist/memberlist.go b/internal/discord/channel/message/memberlist/memberlist.go index cae13b3..65f32e7 100644 --- a/internal/discord/channel/message/memberlist/memberlist.go +++ b/internal/discord/channel/message/memberlist/memberlist.go @@ -3,10 +3,10 @@ package memberlist import ( "context" - "github.com/diamondburned/arikawa/gateway" + "github.com/diamondburned/arikawa/v2/gateway" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" - "github.com/diamondburned/ningen/states/member" + "github.com/diamondburned/ningen/v2/states/member" ) func seekPrevGroup(l *member.List, ix int) (item, group gateway.GuildMemberListOpItem) { diff --git a/internal/discord/channel/message/memberlist/section.go b/internal/discord/channel/message/memberlist/section.go index 0dc144b..cd8473a 100644 --- a/internal/discord/channel/message/memberlist/section.go +++ b/internal/discord/channel/message/memberlist/section.go @@ -3,8 +3,8 @@ package memberlist import ( "fmt" - "github.com/diamondburned/arikawa/discord" - "github.com/diamondburned/arikawa/gateway" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/gateway" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" "github.com/diamondburned/cchat/text" diff --git a/internal/discord/channel/message/message.go b/internal/discord/channel/message/message.go index 66f9f0e..ba796e6 100644 --- a/internal/discord/channel/message/message.go +++ b/internal/discord/channel/message/message.go @@ -4,8 +4,8 @@ import ( "context" "sort" - "github.com/diamondburned/arikawa/discord" - "github.com/diamondburned/arikawa/gateway" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/gateway" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/message/action" "github.com/diamondburned/cchat-discord/internal/discord/channel/message/backlog" diff --git a/internal/discord/channel/message/nickname/nicknamer.go b/internal/discord/channel/message/nickname/nicknamer.go index 9c2acba..bf10a24 100644 --- a/internal/discord/channel/message/nickname/nicknamer.go +++ b/internal/discord/channel/message/nickname/nicknamer.go @@ -2,74 +2,91 @@ package nickname import ( "context" + "fmt" - "github.com/diamondburned/arikawa/gateway" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/gateway" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" + "github.com/diamondburned/cchat-discord/internal/funcutil" "github.com/diamondburned/cchat-discord/internal/segments/colored" "github.com/diamondburned/cchat/text" - "github.com/pkg/errors" ) type Nicknamer struct { + userID discord.UserID shared.Channel } func New(ch shared.Channel) cchat.Nicknamer { - return Nicknamer{ch} + return NewMember(ch.State.UserID, ch) +} + +func NewMember(userID discord.UserID, ch shared.Channel) cchat.Nicknamer { + return Nicknamer{userID, ch} } func (nn Nicknamer) Nickname(ctx context.Context, labeler cchat.LabelContainer) (func(), error) { // We don't have a nickname if we're not in a guild. if !nn.GuildID.IsValid() { + // Use the current user. + u, err := nn.State.Cabinet.Me() + if err == nil { + labeler.SetLabel(text.Plain(fmt.Sprintf("%s#%s", u.Username, u.Discriminator))) + } + return func() {}, nil } + return funcutil.JoinCancels( + nn.State.AddHandler(func(chunks *gateway.GuildMembersChunkEvent) { + if chunks.GuildID != nn.GuildID { + return + } + for _, member := range chunks.Members { + if member.User.ID == nn.userID { + nn.setMember(labeler, member) + break + } + } + }), + nn.State.AddHandler(func(g *gateway.GuildMemberUpdateEvent) { + if g.GuildID == nn.GuildID && g.User.ID == nn.userID { + nn.setMember(labeler, discord.Member{ + User: g.User, + Nick: g.Nick, + RoleIDs: g.RoleIDs, + }) + } + }), + ), nil +} + +func (nn Nicknamer) tryNicknameLabel(ctx context.Context, labeler cchat.LabelContainer) { state := nn.State.WithContext(ctx) - // MemberColor should fill up the state cache. - c, err := state.MemberColor(nn.GuildID, nn.State.UserID) + m, err := state.Cabinet.Member(nn.GuildID, nn.userID) if err != nil { - return nil, errors.Wrap(err, "Failed to get self member color") + return } - m, err := state.Member(nn.GuildID, nn.State.UserID) - if err != nil { - return nil, errors.Wrap(err, "Failed to get self member") - } + nn.setMember(labeler, *m) +} +func (nn Nicknamer) setMember(labeler cchat.LabelContainer, m discord.Member) { var rich = text.Rich{Content: m.User.Username} if m.Nick != "" { rich.Content = m.Nick } - if c > 0 { - rich.Segments = []text.Segment{ - colored.New(len(rich.Content), c.Uint32()), + + guild, err := nn.State.Cabinet.Guild(nn.GuildID) + if err == nil { + if color := discord.MemberColor(*guild, m); color > 0 { + rich.Segments = []text.Segment{ + colored.New(len(rich.Content), color.Uint32()), + } } } labeler.SetLabel(rich) - - // Copy the user ID to use. - var selfID = m.User.ID - - return nn.State.AddHandler(func(g *gateway.GuildMemberUpdateEvent) { - if g.GuildID != nn.GuildID || g.User.ID != selfID { - return - } - - var rich = text.Rich{Content: m.User.Username} - if m.Nick != "" { - rich.Content = m.Nick - } - - c, err := nn.State.MemberColor(g.GuildID, selfID) - if err == nil { - rich.Segments = []text.Segment{ - colored.New(len(rich.Content), c.Uint32()), - } - } - - labeler.SetLabel(rich) - }), nil } diff --git a/internal/discord/channel/message/send/complete/channel.go b/internal/discord/channel/message/send/complete/channel.go index 14df9c3..f378484 100644 --- a/internal/discord/channel/message/send/complete/channel.go +++ b/internal/discord/channel/message/send/complete/channel.go @@ -1,7 +1,7 @@ package complete import ( - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" "github.com/diamondburned/cchat-discord/internal/discord/state" @@ -19,7 +19,7 @@ func (ch ChannelCompleter) CompleteChannels(word string) []cchat.CompletionEntry return nil } - c, err := ch.State.Store.Channels(ch.GuildID) + c, err := ch.State.Cabinet.Channels(ch.GuildID) if err != nil { return nil } @@ -28,7 +28,7 @@ func (ch ChannelCompleter) CompleteChannels(word string) []cchat.CompletionEntry } func DMChannels(s *state.Instance, word string) []cchat.CompletionEntry { - channels, err := s.Store.PrivateChannels() + channels, err := s.Cabinet.PrivateChannels() if err != nil { return nil } @@ -40,7 +40,7 @@ func DMChannels(s *state.Instance, word string) []cchat.CompletionEntry { func rankChannel(word string, ch discord.Channel) int { switch ch.Type { case discord.GroupDM, discord.DirectMessage: - return rankFunc(word, ch.Name+" "+shared.PrivateName(ch)) + return rankFunc(word, ch.Name+" "+shared.ChannelName(ch)) default: return rankFunc(word, ch.Name) } @@ -60,7 +60,7 @@ func completeChannels( var category string if s != nil && channel.CategoryID.IsValid() { - if cat, _ := s.Store.Channel(channel.CategoryID); cat != nil { + if cat, _ := s.Cabinet.Channel(channel.CategoryID); cat != nil { category = cat.Name } } @@ -86,5 +86,4 @@ func completeChannels( sortDistances(entries, distances) return entries - } diff --git a/internal/discord/channel/message/send/complete/completer.go b/internal/discord/channel/message/send/complete/completer.go index 7e55e46..a40270f 100644 --- a/internal/discord/channel/message/send/complete/completer.go +++ b/internal/discord/channel/message/send/complete/completer.go @@ -14,16 +14,23 @@ type ChannelCompleter struct { shared.Channel } -type Completer map[byte]CompleterFunc +type CompleterPrefixes map[byte]CompleterFunc + +type Completer struct { + Prefixes CompleterPrefixes + SlashHandler cchat.Completer +} const MaxCompletion = 15 func New(ch shared.Channel) cchat.Completer { completer := ChannelCompleter{ch} return Completer{ - '@': completer.CompleteMentions, - '#': completer.CompleteChannels, - ':': completer.CompleteEmojis, + Prefixes: map[byte]CompleterFunc{ + '@': completer.CompleteMentions, + '#': completer.CompleteChannels, + ':': completer.CompleteEmojis, + }, } } @@ -31,20 +38,24 @@ func New(ch shared.Channel) cchat.Completer { // This method supports user mentions, channel mentions and emojis. // // For the individual implementations, refer to channel_completion.go. -func (ch Completer) Complete(words []string, i int64) []cchat.CompletionEntry { +func (cc Completer) Complete(words []string, i int64) []cchat.CompletionEntry { var word = words[i] // Word should have at least a character for the char check. - if len(word) < 1 { + if len(word) == 0 { return nil } - fn, ok := ch[word[0]] + // Always check the first word for slash, not the current word. + if cc.SlashHandler != nil && words[0][0] == '/' { + return cc.SlashHandler.Complete(words, i) + } + + fn, ok := cc.Prefixes[word[0]] if !ok { return nil } - fn(word[1:]) - return nil + return fn(word[1:]) } // rankFunc is the default rank function to use. diff --git a/internal/discord/channel/message/send/complete/emoji.go b/internal/discord/channel/message/send/complete/emoji.go index 233e75c..5e0f0a4 100644 --- a/internal/discord/channel/message/send/complete/emoji.go +++ b/internal/discord/channel/message/send/complete/emoji.go @@ -1,7 +1,7 @@ package complete import ( - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/state" "github.com/diamondburned/cchat-discord/internal/urlutils" diff --git a/internal/discord/channel/message/send/complete/mention.go b/internal/discord/channel/message/send/complete/mention.go index cbd6b55..74cf417 100644 --- a/internal/discord/channel/message/send/complete/mention.go +++ b/internal/discord/channel/message/send/complete/mention.go @@ -1,9 +1,8 @@ package complete import ( - "strings" - - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/gateway" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/message" "github.com/diamondburned/cchat-discord/internal/discord/state" @@ -29,8 +28,8 @@ func GuildMessageMentions( // Keep track of the number of authors. // TODO: fix excess allocations - var entries []cchat.CompletionEntry - var authors map[discord.UserID]struct{} + var entries = make([]cchat.CompletionEntry, 0, MaxCompletion) + var authors = make(map[discord.UserID]struct{}, MaxCompletion) for _, msg := range msgs { // If we've already added the author into the list, then skip. @@ -38,13 +37,12 @@ func GuildMessageMentions( continue } - ensureAuthorMapMade(&authors) authors[msg.Author.ID] = struct{}{} var rich text.Rich if guild != nil && state != nil { - m, err := state.Store.Member(guild.ID, msg.Author.ID) + m, err := state.Cabinet.Member(guild.ID, msg.Author.ID) if err == nil { rich = message.RenderMemberName(*m, *guild, state) } @@ -55,8 +53,6 @@ func GuildMessageMentions( rich = text.Plain(msg.Author.Username) } - ensureEntriesMade(&entries) - entries = append(entries, cchat.CompletionEntry{ Raw: msg.Author.Mention(), Text: rich, @@ -72,72 +68,131 @@ func GuildMessageMentions( return entries } -func ensureAuthorMapMade(authors *map[discord.UserID]struct{}) { - if *authors == nil { - *authors = make(map[discord.UserID]struct{}, MaxCompletion) - } -} - -func Presences(s *state.Instance, word string) []cchat.CompletionEntry { - presences, err := s.Presences(0) - if err != nil { - return nil - } +// AllUsers checks for friends and presences. +func AllUsers(s *state.Instance, word string) []cchat.CompletionEntry { + var full bool + var friends map[discord.UserID]struct{} var entries []cchat.CompletionEntry var distances map[string]int - for _, presence := range presences { - rank := rankFunc(word, presence.User.Username) + // Search for friends first. + s.RelationshipState.Each(func(r *discord.Relationship) bool { + // Skip blocked users or strangers. + if r.Type == 0 || r.Type == discord.BlockedRelationship { + return false + } + + rank := rankFunc(word, r.User.Username) if rank == -1 { - continue + return false + } + + if friends == nil { + friends = map[discord.UserID]struct{}{} + } + + friends[r.UserID] = struct{}{} + + ensureEntriesMade(&entries) + ensureDistancesMade(&distances) + + raw := r.User.Mention() + + var status = gateway.UnknownStatus + if p, _ := s.PresenceState.Presence(0, r.UserID); p != nil { + status = p.Status + } + + entries = append(entries, cchat.CompletionEntry{ + Raw: raw, + Text: text.Plain(r.User.Username + "#" + r.User.Discriminator), + Secondary: text.Plain(FormatStatus(status) + " - " + FormatRelationshipType(r.Type)), + IconURL: r.User.AvatarURL(), + }) + + distances[raw] = rank + + full = len(entries) >= MaxCompletion + return full + }) + + if full { + goto Full + } + + // Search for presences. + s.PresenceState.Each(0, func(p *gateway.Presence) bool { + // Avoid duplicates. + if _, ok := friends[p.User.ID]; ok { + return false + } + + rank := rankFunc(word, p.User.Username) + if rank == -1 { + return false } ensureEntriesMade(&entries) ensureDistancesMade(&distances) - raw := presence.User.Mention() + raw := p.User.Mention() entries = append(entries, cchat.CompletionEntry{ Raw: raw, - Text: text.Plain(presence.User.Username + "#" + presence.User.Discriminator), - Secondary: text.Plain(FormatStatus(presence.Status)), - IconURL: presence.User.AvatarURL(), + Text: text.Plain(p.User.Username + "#" + p.User.Discriminator), + Secondary: text.Plain(FormatStatus(p.Status)), + IconURL: p.User.AvatarURL(), }) distances[raw] = rank - if len(entries) >= MaxCompletion { - break - } - } + full = len(entries) >= MaxCompletion + return full + }) +Full: sortDistances(entries, distances) return entries } -func FormatStatus(status discord.Status) string { +func FormatStatus(status gateway.Status) string { switch status { - case discord.OnlineStatus: + case gateway.OnlineStatus: return "Online" - case discord.DoNotDisturbStatus: + case gateway.DoNotDisturbStatus: return "Busy" - case discord.IdleStatus: + case gateway.IdleStatus: return "Idle" - case discord.InvisibleStatus: + case gateway.InvisibleStatus: return "Invisible" - case discord.OfflineStatus: - return "Offline" + case gateway.OfflineStatus: + fallthrough default: - return strings.Title(string(status)) + return "Offline" + } +} + +func FormatRelationshipType(relaType discord.RelationshipType) string { + switch relaType { + case discord.BlockedRelationship: + return "Blocked" + case discord.FriendRelationship: + return "Friend" + case discord.IncomingFriendRequest: + return "Incoming friend request" + case discord.SentFriendRequest: + return "Friend request sent" + default: + return "" } } func (ch ChannelCompleter) CompleteMentions(word string) []cchat.CompletionEntry { // If there is no input, then we should grab the latest messages. if word == "" { - msgs, _ := ch.State.Store.Messages(ch.ID) - g, _ := ch.State.Store.Guild(ch.GuildID) // nil is fine + msgs, _ := ch.State.Cabinet.Messages(ch.ID) + g, _ := ch.State.Cabinet.Guild(ch.GuildID) // nil is fine return GuildMessageMentions(msgs, ch.State, g) } @@ -147,7 +202,7 @@ func (ch ChannelCompleter) CompleteMentions(word string) []cchat.CompletionEntry // If we're not in a guild, then we can check the list of recipients. if !ch.GuildID.IsValid() { - c, err := ch.State.Store.Channel(ch.ID) + c, err := ch.State.Cabinet.Channel(ch.ID) if err != nil { return nil } @@ -182,8 +237,8 @@ func (ch ChannelCompleter) CompleteMentions(word string) []cchat.CompletionEntry } // If we're in a guild, then we should search for (all) members. - m, merr := ch.State.Store.Members(ch.GuildID) - g, gerr := ch.State.Store.Guild(ch.GuildID) + m, merr := ch.State.Cabinet.Members(ch.GuildID) + g, gerr := ch.State.Cabinet.Guild(ch.GuildID) if merr != nil || gerr != nil { return nil diff --git a/internal/discord/channel/message/send/sender.go b/internal/discord/channel/message/send/sender.go index 010aaca..1ee8ef0 100644 --- a/internal/discord/channel/message/send/sender.go +++ b/internal/discord/channel/message/send/sender.go @@ -1,8 +1,9 @@ package send import ( - "github.com/diamondburned/arikawa/api" - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/api" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/utils/sendpart" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/message/send/complete" "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" @@ -50,10 +51,10 @@ func (s Sender) AsCompleter() cchat.Completer { return complete.New(s.Channel) } -func addAttachments(atts []cchat.MessageAttachment) []api.SendMessageFile { - var files = make([]api.SendMessageFile, len(atts)) +func addAttachments(atts []cchat.MessageAttachment) []sendpart.File { + var files = make([]sendpart.File, len(atts)) for i, a := range atts { - files[i] = api.SendMessageFile{ + files[i] = sendpart.File{ Name: a.Name, Reader: a, } diff --git a/internal/discord/channel/private.go b/internal/discord/channel/private.go deleted file mode 100644 index 9d5b15e..0000000 --- a/internal/discord/channel/private.go +++ /dev/null @@ -1,75 +0,0 @@ -package channel - -import ( - "context" - - "github.com/diamondburned/arikawa/discord" - "github.com/diamondburned/arikawa/gateway" - "github.com/diamondburned/cchat" - "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" - "github.com/diamondburned/cchat-discord/internal/discord/state" - "github.com/diamondburned/cchat-discord/internal/urlutils" - "github.com/diamondburned/cchat/text" - "github.com/pkg/errors" -) - -type Private struct { - Channel -} - -var _ cchat.Server = (*Private)(nil) - -func NewPrivate(s *state.Instance, ch discord.Channel) (cchat.Server, error) { - if ch.GuildID.IsValid() { - return nil, errors.New("channel has valid guild ID: not a DM") - } - - channel, err := NewChannel(s, ch) - if err != nil { - return nil, err - } - - return Private{Channel: channel}, nil -} - -func (priv Private) Name() text.Rich { - c, err := priv.Self() - if err != nil { - return text.Rich{Content: priv.ID()} - } - - return text.Plain(shared.PrivateName(*c)) -} - -func (priv Private) AsIconer() cchat.Iconer { - return NewAvatarIcon(priv.State) -} - -type AvatarIcon struct { - State *state.Instance -} - -func NewAvatarIcon(state *state.Instance) cchat.Iconer { - return AvatarIcon{state} -} - -func (avy AvatarIcon) Icon(ctx context.Context, iconer cchat.IconContainer) (func(), error) { - u, err := avy.State.WithContext(ctx).Me() - if err != nil { - // This shouldn't happen. - return nil, errors.Wrap(err, "Failed to get guild") - } - - // Used for comparison. - if u.Avatar != "" { - iconer.SetIcon(urlutils.AvatarURL(u.AvatarURL())) - } - - selfID := u.ID - - return avy.State.AddHandler(func(update *gateway.UserUpdateEvent) { - if selfID == update.ID { - iconer.SetIcon(urlutils.AvatarURL(update.AvatarURL())) - } - }), nil -} diff --git a/internal/discord/channel/shared/channel.go b/internal/discord/channel/shared/channel.go index fa33ed8..ca6b935 100644 --- a/internal/discord/channel/shared/channel.go +++ b/internal/discord/channel/shared/channel.go @@ -5,18 +5,32 @@ import ( "errors" "strings" - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat-discord/internal/discord/state" ) -// PrivateName returns the channel name if any, otherwise it formats its own +// ChannelName returns the channel name if any, otherwise it formats its own // name into a list of recipients. -func PrivateName(privCh discord.Channel) string { - if privCh.Name != "" { - return privCh.Name +func ChannelName(ch discord.Channel) string { + switch ch.Type { + case discord.DirectMessage, discord.GroupDM: + if len(ch.DMRecipients) > 0 { + return FormatRecipients(ch.DMRecipients) + } + + default: + if ch.Name == "" { + break + } + + if ch.NSFW { + return "#" + ch.Name + " (nsfw)" + } else { + return "#" + ch.Name + } } - return FormatRecipients(privCh.DMRecipients) + return ch.ID.String() } // FormatRecipients joins the given list of users into a string listing all @@ -24,14 +38,14 @@ func PrivateName(privCh discord.Channel) string { func FormatRecipients(users []discord.User) string { switch len(users) { case 0: - return "" + return "" case 1: return users[0].Username case 2: return users[0].Username + " and " + users[1].Username } - var usernames = make([]string, len(users)) + var usernames = make([]string, len(users)-1) for i, user := range users[:len(users)-1] { usernames[i] = user.Username } @@ -48,6 +62,11 @@ type Channel struct { // HasPermission returns true if the current user has the given permissions in // the channel. func (ch Channel) HasPermission(perms ...discord.Permissions) bool { + // Assume we have permissions in a direct message channel. + if !ch.GuildID.IsValid() { + return true + } + p, err := ch.State.StateOnly().Permissions(ch.ID, ch.State.UserID) if err != nil { return false @@ -63,16 +82,16 @@ func (ch Channel) HasPermission(perms ...discord.Permissions) bool { } func (ch Channel) Messages() ([]discord.Message, error) { - return ch.State.Store.Messages(ch.ID) + return ch.State.Cabinet.Messages(ch.ID) } func (ch Channel) Guild() (*discord.Guild, error) { if !ch.GuildID.IsValid() { return nil, errors.New("channel not in guild") } - return ch.State.Store.Guild(ch.GuildID) + return ch.State.Cabinet.Guild(ch.GuildID) } func (ch Channel) Self() (*discord.Channel, error) { - return ch.State.Store.Channel(ch.ID) + return ch.State.Cabinet.Channel(ch.ID) } diff --git a/internal/discord/channel/typer/typer.go b/internal/discord/channel/typer/typer.go index 97e0021..0d78e68 100644 --- a/internal/discord/channel/typer/typer.go +++ b/internal/discord/channel/typer/typer.go @@ -4,8 +4,8 @@ import ( "errors" "time" - "github.com/diamondburned/arikawa/discord" - "github.com/diamondburned/arikawa/gateway" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/gateway" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/message" "github.com/diamondburned/cchat-discord/internal/discord/state" @@ -27,13 +27,13 @@ func NewFromAuthor(author message.Author, ev *gateway.TypingStartEvent) Typer { func New(s *state.Instance, ev *gateway.TypingStartEvent) (*Typer, error) { if ev.GuildID.IsValid() { - g, err := s.Store.Guild(ev.GuildID) + g, err := s.Cabinet.Guild(ev.GuildID) if err != nil { return nil, err } if ev.Member == nil { - ev.Member, err = s.Store.Member(ev.GuildID, ev.UserID) + ev.Member, err = s.Cabinet.Member(ev.GuildID, ev.UserID) if err != nil { return nil, err } @@ -45,7 +45,7 @@ func New(s *state.Instance, ev *gateway.TypingStartEvent) (*Typer, error) { }, nil } - c, err := s.Store.Channel(ev.ChannelID) + c, err := s.Cabinet.Channel(ev.ChannelID) if err != nil { return nil, err } diff --git a/internal/discord/folder/folder.go b/internal/discord/folder/folder.go index 37b409f..328fbcd 100644 --- a/internal/discord/folder/folder.go +++ b/internal/discord/folder/folder.go @@ -4,7 +4,7 @@ import ( "strconv" "strings" - "github.com/diamondburned/arikawa/gateway" + "github.com/diamondburned/arikawa/v2/gateway" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/guild" "github.com/diamondburned/cchat-discord/internal/discord/state" @@ -25,7 +25,7 @@ func New(s *state.Instance, gf gateway.GuildFolder) cchat.Server { var names = make([]string, 0, len(gf.GuildIDs)) for _, id := range gf.GuildIDs { - g, err := s.Store.Guild(id) + g, err := s.Cabinet.Guild(id) if err == nil { names = append(names, g.Name) } diff --git a/internal/discord/guild/guild.go b/internal/discord/guild/guild.go index c0ec9f8..20c8070 100644 --- a/internal/discord/guild/guild.go +++ b/internal/discord/guild/guild.go @@ -4,8 +4,8 @@ import ( "context" "sort" - "github.com/diamondburned/arikawa/discord" - "github.com/diamondburned/arikawa/gateway" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/gateway" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/category" "github.com/diamondburned/cchat-discord/internal/discord/channel" @@ -43,7 +43,7 @@ func (g *Guild) self(ctx context.Context) (*discord.Guild, error) { } func (g *Guild) selfState() (*discord.Guild, error) { - return g.state.Store.Guild(g.id) + return g.state.Cabinet.Guild(g.id) } func (g *Guild) ID() cchat.ID { diff --git a/internal/discord/message/author.go b/internal/discord/message/author.go index 3675fb6..674362e 100644 --- a/internal/discord/message/author.go +++ b/internal/discord/message/author.go @@ -1,7 +1,7 @@ package message import ( - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/state" "github.com/diamondburned/cchat-discord/internal/segments/colored" @@ -132,25 +132,31 @@ func (a *Author) AddReply(name string) { // richMember(&a.name, m, g, s) // } +func (a *Author) addAuthorReference(msgref discord.Message, s *state.Instance) { + a.name.Content += authorReplyingTo + start, end := richUser(&a.name, msgref.Author, s) + + a.name.Segments = append(a.name.Segments, + reference.NewMessageSegment(start, end, msgref.ID), + ) +} + // AddMessageReference adds a message reference to the author. func (a *Author) AddMessageReference(msgref discord.Message, s *state.Instance) { if !msgref.GuildID.IsValid() { - a.name.Content += authorReplyingTo - start, end := richUser(&a.name, msgref.Author, s) - - a.name.Segments = append(a.name.Segments, - reference.NewMessageSegment(start, end, msgref.ID), - ) + a.addAuthorReference(msgref, s) return } - g, err := s.Guild(msgref.GuildID) + g, err := s.Cabinet.Guild(msgref.GuildID) if err != nil { + a.addAuthorReference(msgref, s) return } - m, err := s.Member(g.ID, msgref.Author.ID) + m, err := s.Cabinet.Member(g.ID, msgref.Author.ID) if err != nil { + a.addAuthorReference(msgref, s) return } diff --git a/internal/discord/message/message.go b/internal/discord/message/message.go index e697fcb..a1528e8 100644 --- a/internal/discord/message/message.go +++ b/internal/discord/message/message.go @@ -3,8 +3,8 @@ package message import ( "time" - "github.com/diamondburned/arikawa/discord" - "github.com/diamondburned/arikawa/gateway" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/gateway" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/state" "github.com/diamondburned/cchat-discord/internal/segments" @@ -85,13 +85,13 @@ func NewMessageUpdateContent(msg discord.Message, s *state.Instance) Message { // Check if content is empty. if msg.Content == "" { // Then grab the content from the state. - m, err := s.Store.Message(msg.ChannelID, msg.ID) + m, err := s.Cabinet.Message(msg.ChannelID, msg.ID) if err == nil { msg.Content = m.Content } } - var content = segments.ParseMessage(&msg, s.Store) + var content = segments.ParseMessage(&msg, s.Cabinet) return Message{ messageHeader: newHeader(msg), content: content, @@ -116,13 +116,13 @@ func NewGuildMessageCreate(c *gateway.MessageCreateEvent, s *state.Instance) Mes message.Nonce = s.Nonces.Load(c.Nonce) // This should not error. - g, err := s.Store.Guild(c.GuildID) + g, err := s.Cabinet.Guild(c.GuildID) if err != nil { return NewMessage(message, s, NewUser(c.Author, s)) } if c.Member == nil { - c.Member, _ = s.Store.Member(c.GuildID, c.Author.ID) + c.Member, _ = s.Cabinet.Member(c.GuildID, c.Author.ID) } if c.Member == nil { s.MemberState.RequestMember(c.GuildID, c.Author.ID) @@ -142,7 +142,7 @@ func NewBacklogMessage(m discord.Message, s *state.Instance, g discord.Guild) Me return NewMessage(m, s, NewUser(m.Author, s)) } - mem, err := s.Store.Member(m.GuildID, m.Author.ID) + mem, err := s.Cabinet.Member(m.GuildID, m.Author.ID) if err != nil { s.MemberState.RequestMember(m.GuildID, m.Author.ID) return NewMessage(m, s, NewUser(m.Author, s)) @@ -157,7 +157,7 @@ func NewDirectMessage(m discord.Message, s *state.Instance) Message { func NewMessage(m discord.Message, s *state.Instance, author Author) Message { // Render the message content. - var content = segments.ParseMessage(&m, s.Store) + var content = segments.ParseMessage(&m, s.Cabinet) // Request members in mentions if we're in a guild. if m.GuildID.IsValid() { diff --git a/internal/discord/private/hub/messages.go b/internal/discord/private/hub/messages.go index 5efdd4a..e0611a0 100644 --- a/internal/discord/private/hub/messages.go +++ b/internal/discord/private/hub/messages.go @@ -4,8 +4,8 @@ import ( "context" "sync" - "github.com/diamondburned/arikawa/discord" - "github.com/diamondburned/arikawa/gateway" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/gateway" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/message/send/complete" "github.com/diamondburned/cchat-discord/internal/discord/channel/shared" @@ -83,13 +83,13 @@ func NewMessages(s *state.Instance, acList *activeList, adder ChannelAdder) *Mes messages: make(messageList, 0, 100), } - hubServer.sender.completers = complete.Completer{ + hubServer.sender.completers.Prefixes = complete.CompleterPrefixes{ ':': func(word string) []cchat.CompletionEntry { return complete.Emojis(s, 0, word) }, '@': func(word string) []cchat.CompletionEntry { if word != "" { - return complete.Presences(s, word) + return complete.AllUsers(s, word) } hubServer.msgMutex.Lock() @@ -175,7 +175,7 @@ func (msgs *Messages) JoinServer(ctx context.Context, ct cchat.MessagesContainer case discord.DirectMessage: author.AddUserReply(c.DMRecipients[0], msgs.state) case discord.GroupDM: - author.AddReply(shared.PrivateName(*c)) + author.AddReply(shared.ChannelName(*c)) } } } diff --git a/internal/discord/private/hub/sender.go b/internal/discord/private/hub/sender.go index 5b740f2..27ede8b 100644 --- a/internal/discord/private/hub/sender.go +++ b/internal/discord/private/hub/sender.go @@ -4,7 +4,7 @@ import ( "regexp" "strings" - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel/message/send" "github.com/diamondburned/cchat-discord/internal/discord/channel/message/send/complete" @@ -61,8 +61,6 @@ func (s *Sender) Send(sendable cchat.SendableMessage) error { return errors.New("message must start with a user or channel mention") } - // TODO: account for channel names - targetID, err := discord.ParseSnowflake(matches[2]) if err != nil { return errors.Wrap(err, "failed to parse recipient ID") @@ -79,8 +77,17 @@ func (s *Sender) Send(sendable cchat.SendableMessage) error { return errors.New("unknown channel") } - s.adder.AddChannel(s.state, channel) - s.acList.add(channel.ID) + switch channel.Type { + case discord.DirectMessage, discord.GroupDM: + // valid + default: + return errors.New("not a [group] direct message channel") + } + + // We should only add the channel if it's not already in the active list. + if s.acList.add(channel.ID) { + s.adder.AddChannel(s.state, channel) + } sendData := send.WrapMessage(s.state, sendable) sendData.Content = strings.TrimPrefix(content, matches[0]) diff --git a/internal/discord/private/hub/server.go b/internal/discord/private/hub/server.go index 53faaf0..ddae0eb 100644 --- a/internal/discord/private/hub/server.go +++ b/internal/discord/private/hub/server.go @@ -4,7 +4,7 @@ import ( "sync" "time" - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/state" "github.com/diamondburned/cchat/text" @@ -12,8 +12,8 @@ import ( "github.com/pkg/errors" ) -// automatically add all channels with active messages within the past 48 hours. -const autoAddActive = 24 * time.Hour +// automatically add all channels with active messages within the past 5 days. +const autoAddActive = 5 * 24 * time.Hour // activeList contains a list of channel IDs that should be put into its own // channels. @@ -32,7 +32,14 @@ func makeActiveList(s *state.Instance) (*activeList, error) { now := time.Now() for _, channel := range channels { - if channel.LastMessageID.Time().Add(autoAddActive).After(now) { + switch channel.Type { + case discord.DirectMessage, discord.GroupDM: + // valid + default: + continue + } + + if channelIsActive(s, channel, now) { ids[channel.ID] = struct{}{} } } @@ -40,6 +47,42 @@ func makeActiveList(s *state.Instance) (*activeList, error) { return &activeList{active: ids}, nil } +func channelIsActive(s *state.Instance, ch discord.Channel, now time.Time) bool { + // Never show a muted channel, unless requested. + muted := s.MutedState.Channel(ch.ID) + if muted { + return false + } + + read := s.ReadState.FindLast(ch.ID) + + // recently created channel + if ch.ID.Time().Add(autoAddActive).After(now) { + return true + } + + var lastMsg discord.MessageID + if read != nil && read.LastMessageID.IsValid() { + lastMsg = read.LastMessageID + } + if ch.LastMessageID > lastMsg { + // We have a valid message ID in the read state and it is smaller than + // the last message in the channel, so this channel is not read. + if lastMsg.IsValid() { + return true + } + + lastMsg = ch.LastMessageID + } + + // last message is recent + if lastMsg.IsValid() && lastMsg.Time().Add(autoAddActive).After(now) { + return true + } + + return false +} + func (acList *activeList) list() []discord.ChannelID { acList.mut.Lock() defer acList.mut.Unlock() diff --git a/internal/discord/private/private.go b/internal/discord/private/private.go index 83aaa6e..ba3eaa0 100644 --- a/internal/discord/private/private.go +++ b/internal/discord/private/private.go @@ -4,7 +4,8 @@ import ( "sort" "sync" - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/gateway" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/channel" "github.com/diamondburned/cchat-discord/internal/discord/private/hub" @@ -37,8 +38,13 @@ func (cset *containerSet) Register(container cchat.ServersContainer) { // top of the servers container. type prependServer struct{ cchat.Server } +var _ cchat.ServerUpdate = (*prependServer)(nil) + // PreviousID returns the appropriate parameters to prepend this server. -func (ps prependServer) PreviousID() (cchat.ID, bool) { return "", false } +func (ps prependServer) PreviousID() (cchat.ID, bool) { + // Return the private container's ID so this server goes right after it. + return "!!!private-container!!!", false +} func (cset *containerSet) AddChannel(s *state.Instance, ch *discord.Channel) { c, err := channel.New(s, *ch) @@ -90,9 +96,29 @@ func (priv Private) Name() text.Rich { func (priv Private) AsLister() cchat.Lister { return priv } +type activeChannel struct { + *discord.Channel + *gateway.ReadState // used for sorting +} + +func (active activeChannel) LastMessageID() discord.MessageID { + if active.ReadState == nil { + return active.Channel.LastMessageID + } + if active.ReadState.LastMessageID > active.Channel.LastMessageID { + return active.ReadState.LastMessageID + } + if active.Channel.LastMessageID.IsValid() { + return active.Channel.LastMessageID + } + // Whatever. + return discord.MessageID(active.Channel.ID) +} + func (priv Private) Servers(container cchat.ServersContainer) error { activeIDs := priv.hub.ActiveChannelIDs() - channels := make([]*discord.Channel, 0, len(activeIDs)) + + channels := make([]activeChannel, 0, len(activeIDs)) for _, id := range activeIDs { c, err := priv.state.Channel(id) @@ -100,25 +126,28 @@ func (priv Private) Servers(container cchat.ServersContainer) error { return errors.Wrap(err, "failed to get private channel") } - channels = append(channels, c) + channels = append(channels, activeChannel{ + Channel: c, + ReadState: priv.state.ReadState.FindLast(id), + }) } // Sort so that channels with the largest last message ID (and therefore the // latest message) will be on top. sort.Slice(channels, func(i, j int) bool { - return channels[i].LastMessageID > channels[j].LastMessageID + return channels[i].LastMessageID() > channels[j].LastMessageID() }) servers := make([]cchat.Server, len(channels)+1) servers[0] = priv.hub for i, ch := range channels { - c, err := channel.New(priv.state, *ch) + c, err := channel.New(priv.state, *ch.Channel) if err != nil { return errors.Wrap(err, "failed to create server for private channel") } - servers[i] = c + servers[i+1] = c } container.SetServers(servers) diff --git a/internal/discord/session/session.go b/internal/discord/session/session.go index 8b4ced8..ccd1183 100644 --- a/internal/discord/session/session.go +++ b/internal/discord/session/session.go @@ -3,8 +3,8 @@ package session import ( "context" - "github.com/diamondburned/arikawa/gateway" - "github.com/diamondburned/arikawa/session" + "github.com/diamondburned/arikawa/v2/gateway" + "github.com/diamondburned/arikawa/v2/session" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/folder" "github.com/diamondburned/cchat-discord/internal/discord/guild" @@ -13,7 +13,7 @@ import ( "github.com/diamondburned/cchat-discord/internal/urlutils" "github.com/diamondburned/cchat/text" "github.com/diamondburned/cchat/utils/empty" - "github.com/diamondburned/ningen" + "github.com/diamondburned/ningen/v2" "github.com/pkg/errors" ) @@ -42,7 +42,7 @@ func (s *Session) ID() cchat.ID { } func (s *Session) Name() text.Rich { - u, err := s.state.Store.Me() + u, err := s.state.Cabinet.Me() if err != nil { // This shouldn't happen, ever. return text.Rich{Content: "<@" + s.state.UserID.String() + ">"} @@ -64,7 +64,7 @@ func (s *Session) Icon(ctx context.Context, iconer cchat.IconContainer) (func(), return s.state.AddHandler(func(*gateway.UserUpdateEvent) { // Bypass the event and use the state cache. - if u, err := s.state.Store.Me(); err == nil { + if u, err := s.state.Cabinet.Me(); err == nil { iconer.SetIcon(urlutils.AvatarURL(u.AvatarURL())) } }), nil @@ -91,17 +91,16 @@ func (s *Session) Servers(container cchat.ServersContainer) error { } func (s *Session) servers(container cchat.ServersContainer) error { - // TODO: remove this once v2 is used, so we could swap it with a getter. - ready := s.state.Ready + ready := s.state.Ready() switch { // If the user has guild folders: - case len(ready.Settings.GuildFolders) > 0: + case len(ready.UserSettings.GuildFolders) > 0: // TODO: account for missing guilds. - toplevels := make([]cchat.Server, 1, len(ready.Settings.GuildFolders)+1) + toplevels := make([]cchat.Server, 1, len(ready.UserSettings.GuildFolders)+1) toplevels[0] = s.private - for _, guildFolder := range ready.Settings.GuildFolders { + for _, guildFolder := range ready.UserSettings.GuildFolders { // TODO: correct. switch { case guildFolder.ID != 0: @@ -122,11 +121,11 @@ func (s *Session) servers(container cchat.ServersContainer) error { // If the user doesn't have guild folders but has sorted their guilds // before: - case len(ready.Settings.GuildPositions) > 0: - guilds := make([]cchat.Server, 1, len(ready.Settings.GuildPositions)+1) + case len(ready.UserSettings.GuildPositions) > 0: + guilds := make([]cchat.Server, 1, len(ready.UserSettings.GuildPositions)+1) guilds[0] = s.private - for _, id := range ready.Settings.GuildPositions { + for _, id := range ready.UserSettings.GuildPositions { g, err := guild.NewFromID(s.state, id) if err != nil { continue diff --git a/internal/discord/state/state.go b/internal/discord/state/state.go index 676588b..566d152 100644 --- a/internal/discord/state/state.go +++ b/internal/discord/state/state.go @@ -5,13 +5,14 @@ import ( "context" "log" - "github.com/diamondburned/arikawa/discord" - "github.com/diamondburned/arikawa/session" - "github.com/diamondburned/arikawa/state" - "github.com/diamondburned/arikawa/utils/httputil/httpdriver" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/session" + "github.com/diamondburned/arikawa/v2/state" + "github.com/diamondburned/arikawa/v2/state/store/defaultstore" + "github.com/diamondburned/arikawa/v2/utils/httputil/httpdriver" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/state/nonce" - "github.com/diamondburned/ningen" + "github.com/diamondburned/ningen/v2" "github.com/pkg/errors" ) @@ -19,7 +20,8 @@ type Instance struct { *ningen.State Nonces *nonce.Map - // UserID is a constant user ID. It is guaranteed to be valid. + // UserID is a constant user ID of the current user. It is guaranteed to be + // valid. UserID discord.UserID } @@ -52,17 +54,13 @@ func Login(email, password, mfa string) (*Instance, error) { return nil, err } - state, _ := state.NewFromSession(session, state.NewDefaultStore(nil)) - return New(state) + cabinet := defaultstore.New() + cabinet.MessageStore = defaultstore.NewMessage(50) + + return New(state.NewFromSession(session, cabinet)) } func New(s *state.State) (*Instance, error) { - // Prefetch user. - u, err := s.Me() - if err != nil { - return nil, errors.Wrap(err, "failed to get current user") - } - n, err := ningen.FromState(s) if err != nil { return nil, errors.Wrap(err, "failed to create a state wrapper") @@ -77,6 +75,12 @@ func New(s *state.State) (*Instance, error) { return nil, err } + // Prefetch user. + u, err := s.Me() + if err != nil { + return nil, errors.Wrap(err, "failed to get current user") + } + return &Instance{ UserID: u.ID, State: n, diff --git a/internal/segments/embed/avatar.go b/internal/segments/embed/avatar.go index 09590ba..843ad8b 100644 --- a/internal/segments/embed/avatar.go +++ b/internal/segments/embed/avatar.go @@ -1,7 +1,7 @@ package embed import ( - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat-discord/internal/segments/emoji" "github.com/diamondburned/cchat/text" "github.com/diamondburned/cchat/utils/empty" diff --git a/internal/segments/embed/embed.go b/internal/segments/embed/embed.go index 6c34dc6..b57440e 100644 --- a/internal/segments/embed/embed.go +++ b/internal/segments/embed/embed.go @@ -4,15 +4,15 @@ import ( "fmt" "time" - "github.com/diamondburned/arikawa/discord" - "github.com/diamondburned/arikawa/state" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/state/store" "github.com/diamondburned/cchat-discord/internal/segments/colored" "github.com/diamondburned/cchat-discord/internal/segments/inline" "github.com/diamondburned/cchat-discord/internal/segments/link" "github.com/diamondburned/cchat-discord/internal/segments/renderer" "github.com/diamondburned/cchat-discord/internal/urlutils" "github.com/diamondburned/cchat/text" - "github.com/diamondburned/ningen/md" + "github.com/diamondburned/ningen/v2/md" "github.com/dustin/go-humanize" ) @@ -24,7 +24,7 @@ func writeEmbedSep(r *renderer.Text, embedColor discord.Color) { } } -func RenderEmbeds(r *renderer.Text, embeds []discord.Embed, m *discord.Message, s state.Store) { +func RenderEmbeds(r *renderer.Text, embeds []discord.Embed, m *discord.Message, s store.Cabinet) { for _, embed := range embeds { r.StartBlock() writeEmbedSep(r, embed.Color) @@ -38,7 +38,7 @@ func RenderEmbeds(r *renderer.Text, embeds []discord.Embed, m *discord.Message, } } -func RenderEmbed(r *renderer.Text, embed discord.Embed, m *discord.Message, s state.Store) { +func RenderEmbed(r *renderer.Text, embed discord.Embed, m *discord.Message, s store.Cabinet) { if a := embed.Author; a != nil && a.Name != "" { if a.ProxyIcon != "" { r.Append(Author(r.Buffer.Len(), *a)) diff --git a/internal/segments/embed/image.go b/internal/segments/embed/image.go index 14ba251..ac95246 100644 --- a/internal/segments/embed/image.go +++ b/internal/segments/embed/image.go @@ -3,7 +3,7 @@ package embed import ( "fmt" - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat-discord/internal/urlutils" "github.com/diamondburned/cchat/text" "github.com/diamondburned/cchat/utils/empty" diff --git a/internal/segments/emoji/emoji.go b/internal/segments/emoji/emoji.go index 3340ff0..09c2772 100644 --- a/internal/segments/emoji/emoji.go +++ b/internal/segments/emoji/emoji.go @@ -3,11 +3,11 @@ package emoji import ( "net/url" - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat-discord/internal/segments/renderer" "github.com/diamondburned/cchat/text" "github.com/diamondburned/cchat/utils/empty" - "github.com/diamondburned/ningen/md" + "github.com/diamondburned/ningen/v2/md" "github.com/yuin/goldmark/ast" ) diff --git a/internal/segments/inline/inline.go b/internal/segments/inline/inline.go index 34c6cd6..c676946 100644 --- a/internal/segments/inline/inline.go +++ b/internal/segments/inline/inline.go @@ -4,7 +4,7 @@ import ( "github.com/diamondburned/cchat-discord/internal/segments/renderer" "github.com/diamondburned/cchat/text" "github.com/diamondburned/cchat/utils/empty" - "github.com/diamondburned/ningen/md" + "github.com/diamondburned/ningen/v2/md" "github.com/yuin/goldmark/ast" ) @@ -48,6 +48,20 @@ func (attr Attribute) Attribute() text.Attribute { return text.Attribute(attr) } +// DimSuffix creates a string with the suffix dimmed. +func DimSuffix(prefix, suffix string) text.Rich { + return text.Rich{ + Content: prefix + suffix, + Segments: []text.Segment{ + Segment{ + start: len(prefix), + end: len(prefix) + len(suffix), + attributes: Attribute(text.AttributeDimmed), + }, + }, + } +} + type Segment struct { empty.TextSegment start, end int diff --git a/internal/segments/md.go b/internal/segments/md.go index cae4391..15d49aa 100644 --- a/internal/segments/md.go +++ b/internal/segments/md.go @@ -1,12 +1,12 @@ package segments import ( - "github.com/diamondburned/arikawa/discord" - "github.com/diamondburned/arikawa/state" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/state/store" "github.com/diamondburned/cchat-discord/internal/segments/embed" "github.com/diamondburned/cchat-discord/internal/segments/renderer" "github.com/diamondburned/cchat/text" - "github.com/diamondburned/ningen/md" + "github.com/diamondburned/ningen/v2/md" _ "github.com/diamondburned/cchat-discord/internal/segments/blockquote" _ "github.com/diamondburned/cchat-discord/internal/segments/codeblock" @@ -17,7 +17,7 @@ import ( _ "github.com/diamondburned/cchat-discord/internal/segments/mention" ) -func ParseMessage(m *discord.Message, s state.Store) text.Rich { +func ParseMessage(m *discord.Message, s store.Cabinet) text.Rich { var content = []byte(m.Content) var node = md.ParseWithMessage(content, s, m, true) @@ -36,7 +36,7 @@ func ParseMessage(m *discord.Message, s state.Store) text.Rich { } } -func ParseWithMessage(b []byte, m *discord.Message, s state.Store, msg bool) text.Rich { +func ParseWithMessage(b []byte, m *discord.Message, s store.Cabinet, msg bool) text.Rich { node := md.ParseWithMessage(b, s, m, msg) return renderer.RenderNode(b, node) } diff --git a/internal/segments/md_test.go b/internal/segments/md_test.go index 6f362cf..448208d 100644 --- a/internal/segments/md_test.go +++ b/internal/segments/md_test.go @@ -5,8 +5,8 @@ import ( "log" "testing" - "github.com/diamondburned/arikawa/discord" - "github.com/diamondburned/arikawa/state" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/state" "github.com/diamondburned/cchat/text" "github.com/go-test/deep" ) diff --git a/internal/segments/mention/activity.go b/internal/segments/mention/activity.go index e803445..c374a6a 100644 --- a/internal/segments/mention/activity.go +++ b/internal/segments/mention/activity.go @@ -4,14 +4,14 @@ import ( "bytes" "fmt" - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat-discord/internal/segments/emoji" "github.com/diamondburned/cchat-discord/internal/segments/inline" "github.com/diamondburned/cchat-discord/internal/segments/segutil" "github.com/diamondburned/cchat-discord/internal/urlutils" "github.com/diamondburned/cchat/text" "github.com/diamondburned/cchat/utils/empty" - "github.com/diamondburned/ningen" + "github.com/diamondburned/ningen/v2" ) type LargeActivityImage struct { @@ -128,7 +128,7 @@ func getPresence( return &p.Activities[0] } - return p.Game + return nil } func findRole(roles []discord.Role, id discord.RoleID) (discord.Role, bool) { diff --git a/internal/segments/mention/channel.go b/internal/segments/mention/channel.go index dd39f70..9273fae 100644 --- a/internal/segments/mention/channel.go +++ b/internal/segments/mention/channel.go @@ -1,7 +1,7 @@ package mention import ( - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat-discord/internal/segments/renderer" "github.com/diamondburned/cchat/text" ) diff --git a/internal/segments/mention/mention.go b/internal/segments/mention/mention.go index 5da7fd9..7bfa438 100644 --- a/internal/segments/mention/mention.go +++ b/internal/segments/mention/mention.go @@ -4,7 +4,7 @@ import ( "github.com/diamondburned/cchat-discord/internal/segments/renderer" "github.com/diamondburned/cchat/text" "github.com/diamondburned/cchat/utils/empty" - "github.com/diamondburned/ningen/md" + "github.com/diamondburned/ningen/v2/md" "github.com/yuin/goldmark/ast" ) diff --git a/internal/segments/mention/role.go b/internal/segments/mention/role.go index 42dd369..c0c1c6f 100644 --- a/internal/segments/mention/role.go +++ b/internal/segments/mention/role.go @@ -1,7 +1,7 @@ package mention import ( - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat-discord/internal/segments/colored" "github.com/diamondburned/cchat/text" ) diff --git a/internal/segments/mention/user.go b/internal/segments/mention/user.go index 2c99047..12ed841 100644 --- a/internal/segments/mention/user.go +++ b/internal/segments/mention/user.go @@ -4,14 +4,14 @@ import ( "bytes" "strings" - "github.com/diamondburned/arikawa/discord" - "github.com/diamondburned/arikawa/state" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/state/store" "github.com/diamondburned/cchat-discord/internal/segments/colored" "github.com/diamondburned/cchat-discord/internal/segments/inline" "github.com/diamondburned/cchat-discord/internal/segments/segutil" "github.com/diamondburned/cchat/text" "github.com/diamondburned/cchat/utils/empty" - "github.com/diamondburned/ningen" + "github.com/diamondburned/ningen/v2" ) // NameSegment represents a clickable member name; it does not implement colors. @@ -29,6 +29,7 @@ func UserSegment(start, end int, u discord.User) NameSegment { start: start, end: end, um: User{ + store: store.NoopCabinet, Member: discord.Member{User: u}, }, } @@ -39,6 +40,7 @@ func MemberSegment(start, end int, guild discord.Guild, m discord.Member) NameSe start: start, end: end, um: User{ + store: store.NoopCabinet, Guild: guild, Member: m, }, @@ -48,7 +50,7 @@ func MemberSegment(start, end int, guild discord.Guild, m discord.Member) NameSe // WithState assigns a ningen state into the given name segment. This allows the // popovers to have additional information such as user notes. func (m *NameSegment) WithState(state *ningen.State) { - m.um.state = state + m.um.WithState(state) } func (m NameSegment) Bounds() (start, end int) { @@ -67,7 +69,9 @@ func (m NameSegment) AsColorer() text.Colorer { } type User struct { - state state.Store + ningen *ningen.State + store store.Cabinet + Guild discord.Guild Member discord.Member } @@ -80,9 +84,9 @@ var ( // NewUser creates a new user mention. If state is of type *ningen.State, then // it'll fetch additional information asynchronously. -func NewUser(state state.Store, guild discord.GuildID, guser discord.GuildUser) *User { +func NewUser(store store.Cabinet, guild discord.GuildID, guser discord.GuildUser) *User { if guser.Member == nil { - m, err := state.Member(guild, guser.ID) + m, err := store.Member(guild, guser.ID) if err != nil { guser.Member = &discord.Member{} } else { @@ -93,18 +97,22 @@ func NewUser(state state.Store, guild discord.GuildID, guser discord.GuildUser) guser.Member.User = guser.User // Get the guild for the role slice. If not, then too bad. - g, err := state.Guild(guild) + g, err := store.Guild(guild) if err != nil { g = &discord.Guild{} } return &User{ - state: state, + store: store, Guild: *g, Member: *guser.Member, } } +func (um *User) WithState(state *ningen.State) { + um.ningen = state +} + // HasColor returns true if the current user has a color. func (um User) HasColor() bool { // We don't have any member color if we have neither the member nor guild. @@ -112,7 +120,7 @@ func (um User) HasColor() bool { return false } - g, err := um.state.Guild(um.Guild.ID) + g, err := um.store.Guild(um.Guild.ID) if err != nil { return false } @@ -121,7 +129,7 @@ func (um User) HasColor() bool { } func (um User) Color() uint32 { - g, err := um.state.Guild(um.Guild.ID) + g, err := um.store.Guild(um.Guild.ID) if err != nil { return colored.Blurple } @@ -190,9 +198,9 @@ func (um User) MentionInfo() text.Rich { // These information can only be obtained from the state. As such, we check // if the state is given. - if ningenState, ok := um.state.(*ningen.State); ok { + if um.ningen != nil { // Does the user have rich presence? If so, write. - if p, err := um.state.Presence(um.Guild.ID, um.Member.User.ID); err == nil { + if p, err := um.store.Presence(um.Guild.ID, um.Member.User.ID); err == nil { for _, ac := range p.Activities { formatActivity(&segment, &content, ac) content.WriteString("\n\n") @@ -200,11 +208,11 @@ func (um User) MentionInfo() text.Rich { } else if um.Guild.ID.IsValid() { // If we're still in a guild, then we can ask Discord for that // member with their presence attached. - ningenState.MemberState.RequestMember(um.Guild.ID, um.Member.User.ID) + um.ningen.MemberState.RequestMember(um.Guild.ID, um.Member.User.ID) } // Write the user's note if any. - if note := ningenState.NoteState.Note(um.Member.User.ID); note != "" { + if note := um.ningen.NoteState.Note(um.Member.User.ID); note != "" { formatSectionf(&segment, &content, "Note") content.WriteRune('\n') diff --git a/internal/segments/reference/reference.go b/internal/segments/reference/reference.go index 657d563..dd69518 100644 --- a/internal/segments/reference/reference.go +++ b/internal/segments/reference/reference.go @@ -1,7 +1,7 @@ package reference import ( - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat/text" "github.com/diamondburned/cchat/utils/empty" diff --git a/internal/segments/renderer/inline.go b/internal/segments/renderer/inline.go index 1fb962f..916fbec 100644 --- a/internal/segments/renderer/inline.go +++ b/internal/segments/renderer/inline.go @@ -1,7 +1,7 @@ package renderer import ( - "github.com/diamondburned/ningen/md" + "github.com/diamondburned/ningen/v2/md" ) // InlineState assists in keeping a stateful inline segment builder. diff --git a/internal/segments/renderer/renderer.go b/internal/segments/renderer/renderer.go index 0aec966..0a831d7 100644 --- a/internal/segments/renderer/renderer.go +++ b/internal/segments/renderer/renderer.go @@ -3,13 +3,12 @@ package renderer import ( "bytes" "fmt" - "log" - "github.com/diamondburned/arikawa/discord" - "github.com/diamondburned/arikawa/state" + "github.com/diamondburned/arikawa/v2/discord" + "github.com/diamondburned/arikawa/v2/state/store" "github.com/diamondburned/cchat-discord/internal/segments/segutil" "github.com/diamondburned/cchat/text" - "github.com/diamondburned/ningen/md" + "github.com/diamondburned/ningen/v2/md" "github.com/yuin/goldmark/ast" ) @@ -19,7 +18,6 @@ var renderers = map[ast.NodeKind]Renderer{} // Register registers a renderer to a node kind. func Register(kind ast.NodeKind, r Renderer) { - log.Printf("Registering kind %v", kind) renderers[kind] = r } @@ -50,7 +48,7 @@ type Text struct { // these fields can be nil Message *discord.Message - Store state.Store + Store store.Cabinet } func New(src []byte, node ast.Node) *Text { @@ -64,7 +62,7 @@ func New(src []byte, node ast.Node) *Text { } } -func (r *Text) WithState(m *discord.Message, s state.Store) { +func (r *Text) WithState(m *discord.Message, s store.Cabinet) { r.Message = m r.Store = s } diff --git a/internal/urlutils/urlutils.go b/internal/urlutils/urlutils.go index c572dd3..4f500d6 100644 --- a/internal/urlutils/urlutils.go +++ b/internal/urlutils/urlutils.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/v2/discord" ) // AvatarURL wraps the URL with URL queries for the avatar.