Skip to content

Commit

Permalink
Merge pull request #196 from disgoorg/patch/improve-presence-ux
Browse files Browse the repository at this point in the history
  • Loading branch information
mlnrDev committed Sep 4, 2022
2 parents 470216c + 9fad6a4 commit f30ee80
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 52 deletions.
2 changes: 1 addition & 1 deletion _examples/test/examplebot.go
Expand Up @@ -33,7 +33,7 @@ func main() {
client, err := disgo.New(token,
bot.WithGatewayConfigOpts(
gateway.WithIntents(gateway.IntentsNonPrivileged, gateway.IntentMessageContent),
gateway.WithPresence(gateway.NewListeningPresence("your bullshit", discord.OnlineStatusOnline, false)),
gateway.WithPresenceOpts(gateway.WithListeningActivity("your bullshit"), gateway.WithOnlineStatus(discord.OnlineStatusDND)),
),
bot.WithCacheConfigOpts(
cache.WithCacheFlags(cache.FlagsAll),
Expand Down
28 changes: 20 additions & 8 deletions bot/client.go
Expand Up @@ -91,11 +91,11 @@ type Client interface {
// limit : The number of discord.Member(s) to return.
RequestMembersWithQuery(ctx context.Context, guildID snowflake.ID, presence bool, nonce string, query string, limit int) error

// SetPresence sends a discord.MessageDataPresenceUpdate to the gateway.Gateway.
SetPresence(ctx context.Context, presenceUpdate gateway.MessageDataPresenceUpdate) error
// SetPresence sends new presence data to the gateway.Gateway.
SetPresence(ctx context.Context, opts ...gateway.PresenceOpt) error

// SetPresenceForShard sends a discord.MessageDataPresenceUpdate to the specific gateway.Gateway.
SetPresenceForShard(ctx context.Context, shardId int, presenceUpdate gateway.MessageDataPresenceUpdate) error
// SetPresenceForShard sends new presence data to the specific gateway.Gateway.
SetPresenceForShard(ctx context.Context, shardId int, opts ...gateway.PresenceOpt) error

// MemberChunkingManager returns the MemberChunkingManager used by the Client.
MemberChunkingManager() MemberChunkingManager
Expand Down Expand Up @@ -276,22 +276,34 @@ func (c *clientImpl) RequestMembersWithQuery(ctx context.Context, guildID snowfl
})
}

func (c *clientImpl) SetPresence(ctx context.Context, presenceUpdate gateway.MessageDataPresenceUpdate) error {
func (c *clientImpl) SetPresence(ctx context.Context, opts ...gateway.PresenceOpt) error {
if !c.HasGateway() {
return discord.ErrNoGateway
}
return c.gateway.Send(ctx, gateway.OpcodePresenceUpdate, presenceUpdate)
g := c.gateway
return g.Send(ctx, gateway.OpcodePresenceUpdate, applyPresenceFromOpts(g, opts...))
}

func (c *clientImpl) SetPresenceForShard(ctx context.Context, shardId int, presenceUpdate gateway.MessageDataPresenceUpdate) error {
func (c *clientImpl) SetPresenceForShard(ctx context.Context, shardId int, opts ...gateway.PresenceOpt) error {
if !c.HasShardManager() {
return discord.ErrNoShardManager
}
shard := c.shardManager.Shard(shardId)
if shard == nil {
return discord.ErrShardNotFound
}
return shard.Send(ctx, gateway.OpcodePresenceUpdate, presenceUpdate)
return shard.Send(ctx, gateway.OpcodePresenceUpdate, applyPresenceFromOpts(shard, opts...))
}

func applyPresenceFromOpts(g gateway.Gateway, opts ...gateway.PresenceOpt) gateway.MessageDataPresenceUpdate {
presenceUpdate := g.Presence()
if presenceUpdate == nil {
presenceUpdate = &gateway.MessageDataPresenceUpdate{}
}
for _, opt := range opts {
opt(presenceUpdate)
}
return *presenceUpdate
}

func (c *clientImpl) MemberChunkingManager() MemberChunkingManager {
Expand Down
3 changes: 3 additions & 0 deletions gateway/gateway.go
Expand Up @@ -100,4 +100,7 @@ type Gateway interface {
// Latency returns the latency of the Gateway.
// This is calculated by the time it takes to send a heartbeat and receive a heartbeat ack by discord.
Latency() time.Duration

// Presence returns the current presence of the Gateway.
Presence() *MessageDataPresenceUpdate
}
12 changes: 8 additions & 4 deletions gateway/gateway_config.go
Expand Up @@ -179,10 +179,14 @@ func WithRateRateLimiterConfigOpts(opts ...RateLimiterConfigOpt) ConfigOpt {
}
}

// WithPresence sets the initial presence the bot should display.
func WithPresence(presence MessageDataPresenceUpdate) ConfigOpt {
return func(config *Config) {
config.Presence = &presence
// WithPresenceOpts allows to pass initial presence data the bot should display.
func WithPresenceOpts(opts ...PresenceOpt) ConfigOpt {
return func(config *Config) {
presenceUpdate := &MessageDataPresenceUpdate{}
for _, opt := range opts {
opt(presenceUpdate)
}
config.Presence = presenceUpdate
}
}

Expand Down
4 changes: 4 additions & 0 deletions gateway/gateway_impl.go
Expand Up @@ -204,6 +204,10 @@ func (g *gatewayImpl) Latency() time.Duration {
return g.lastHeartbeatReceived.Sub(g.lastHeartbeatSent)
}

func (g *gatewayImpl) Presence() *MessageDataPresenceUpdate {
return g.config.Presence
}

func (g *gatewayImpl) reconnectTry(ctx context.Context, try int, delay time.Duration) error {
if try >= g.config.MaxReconnectTries-1 {
return fmt.Errorf("failed to reconnect. exceeded max reconnect tries of %d reached", g.config.MaxReconnectTries)
Expand Down
97 changes: 58 additions & 39 deletions gateway/gateway_messages.go
Expand Up @@ -2,7 +2,6 @@ package gateway

import (
"fmt"
"time"

"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/json"
Expand Down Expand Up @@ -437,57 +436,77 @@ type IdentifyCommandDataProperties struct {
Device string `json:"device"` // library name
}

// NewPresence creates a new Presence with the provided properties
func NewPresence(activityType discord.ActivityType, name string, url string, status discord.OnlineStatus, afk bool) MessageDataPresenceUpdate {
var since *int64
if status == discord.OnlineStatusIdle {
unix := time.Now().Unix()
since = &unix
}
type PresenceOpt func(presenceUpdate *MessageDataPresenceUpdate)

var activities []discord.Activity
if name != "" {
activity := discord.Activity{
Name: name,
Type: activityType,
}
if activityType == discord.ActivityTypeStreaming && url != "" {
activity.URL = &url
}
activities = append(activities, activity)
}
// WithPlayingActivity creates a new "Playing ..." activity of type discord.ActivityTypeGame
func WithPlayingActivity(name string) PresenceOpt {
return withActivity(discord.Activity{
Name: name,
Type: discord.ActivityTypeGame,
})
}

return MessageDataPresenceUpdate{
Since: since,
Activities: activities,
Status: status,
AFK: afk,
// WithStreamingActivity creates a new "Streaming ..." activity of type discord.ActivityTypeStreaming
func WithStreamingActivity(name string, url string) PresenceOpt {
activity := discord.Activity{
Name: name,
Type: discord.ActivityTypeStreaming,
}
if url != "" {
activity.URL = &url
}
return withActivity(activity)
}

// WithListeningActivity creates a new "Listening to ..." activity of type discord.ActivityTypeListening
func WithListeningActivity(name string) PresenceOpt {
return withActivity(discord.Activity{
Name: name,
Type: discord.ActivityTypeListening,
})
}

// NewGamePresence creates a new Presence of type ActivityTypeGame
func NewGamePresence(name string, status discord.OnlineStatus, afk bool) MessageDataPresenceUpdate {
return NewPresence(discord.ActivityTypeGame, name, "", status, afk)
// WithWatchingActivity creates a new "Watching ..." activity of type discord.ActivityTypeWatching
func WithWatchingActivity(name string) PresenceOpt {
return withActivity(discord.Activity{
Name: name,
Type: discord.ActivityTypeWatching,
})
}

// NewStreamingPresence creates a new Presence of type ActivityTypeStreaming
func NewStreamingPresence(name string, url string, status discord.OnlineStatus, afk bool) MessageDataPresenceUpdate {
return NewPresence(discord.ActivityTypeStreaming, name, url, status, afk)
// WithCompetingActivity creates a new "Competing in ..." activity of type discord.ActivityTypeCompeting
func WithCompetingActivity(name string) PresenceOpt {
return withActivity(discord.Activity{
Name: name,
Type: discord.ActivityTypeCompeting,
})
}

// NewListeningPresence creates a new Presence of type ActivityTypeListening
func NewListeningPresence(name string, status discord.OnlineStatus, afk bool) MessageDataPresenceUpdate {
return NewPresence(discord.ActivityTypeListening, name, "", status, afk)
func withActivity(activity discord.Activity) PresenceOpt {
return func(presence *MessageDataPresenceUpdate) {
presence.Activities = []discord.Activity{activity}
}
}

// WithOnlineStatus sets the online status to the provided discord.OnlineStatus
func WithOnlineStatus(status discord.OnlineStatus) PresenceOpt {
return func(presence *MessageDataPresenceUpdate) {
presence.Status = status
}
}

// NewWatchingPresence creates a new Presence of type ActivityTypeWatching
func NewWatchingPresence(name string, status discord.OnlineStatus, afk bool) MessageDataPresenceUpdate {
return NewPresence(discord.ActivityTypeWatching, name, "", status, afk)
// WithAfk sets whether the session is afk
func WithAfk(afk bool) PresenceOpt {
return func(presence *MessageDataPresenceUpdate) {
presence.AFK = afk
}
}

// NewCompetingPresence creates a new Presence of type ActivityTypeCompeting
func NewCompetingPresence(name string, status discord.OnlineStatus, afk bool) MessageDataPresenceUpdate {
return NewPresence(discord.ActivityTypeCompeting, name, "", status, afk)
// WithSince sets when the session has gone afk
func WithSince(since *int64) PresenceOpt {
return func(presence *MessageDataPresenceUpdate) {
presence.Since = since
}
}

// MessageDataPresenceUpdate is used for updating Client's presence
Expand Down

0 comments on commit f30ee80

Please sign in to comment.