From e3fc322980369dc9a2391f3ea128a8697a8030d9 Mon Sep 17 00:00:00 2001 From: Cynthia Foxwell Date: Sun, 16 Jul 2023 18:05:04 -0600 Subject: [PATCH] presence --- commands/guild.go | 4 +- lib/presence.go | 152 ++++++++++++++++++++++++++++++++++++++++++++++ main.go | 17 ++++++ state/main.go | 6 +- 4 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 lib/presence.go diff --git a/commands/guild.go b/commands/guild.go index 75b132b..97c182c 100644 --- a/commands/guild.go +++ b/commands/guild.go @@ -442,7 +442,7 @@ func SwitchGuild(session *discordgo.Session, input string) { ListChannelsCommand(session) ListUsersCommand(session) - // TODO: update presence + lib.UpdatePresence(session) } } } @@ -486,7 +486,7 @@ func SwitchChannelsCommand(session *discordgo.Session) { ListUsersCommand(session) - // TODO: update presence + lib.UpdatePresence(session) } } }) diff --git a/lib/presence.go b/lib/presence.go new file mode 100644 index 0000000..48409ac --- /dev/null +++ b/lib/presence.go @@ -0,0 +1,152 @@ +package lib + +import ( + "fmt" + "reflect" + "sync" + "time" + "unsafe" + + "github.com/Cynosphere/comcord/state" + "github.com/bwmarrin/discordgo" + "github.com/gorilla/websocket" +) + +type ActivityMetadata struct { + ButtonURLs []string `json:"button_urls,omitempty"` +} + +type Activity struct { + Name string `json:"name"` + Type discordgo.ActivityType `json:"type"` + URL string `json:"url,omitempty"` + CreatedAt time.Time `json:"created_at"` + ApplicationID string `json:"application_id,omitempty"` + State string `json:"state,omitempty"` + Details string `json:"details,omitempty"` + Timestamps discordgo.TimeStamps `json:"timestamps,omitempty"` + Emoji discordgo.Emoji `json:"emoji,omitempty"` + Party discordgo.Party `json:"party,omitempty"` + Assets discordgo.Assets `json:"assets,omitempty"` + Secrets discordgo.Secrets `json:"secrets,omitempty"` + Instance bool `json:"instance,omitempty"` + Flags int `json:"flags,omitempty"` + Buttons []string `json:"buttons,omitempty"` + Metadata ActivityMetadata `json:"metadata,omitempty"` +} + +type GatewayPresenceUpdate struct { + Since int `json:"since"` + Activities []Activity `json:"activities,omitempty"` + Status string `json:"status"` + AFK bool `json:"afk"` + Broadcast string `json:"broadcast,omitempty"` +} + +type presenceOp struct { + Op int `json:"op"` + Data GatewayPresenceUpdate `json:"d"` +} + +func getUnexportedField(field reflect.Value) interface{} { + return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface() +} + +func UpdatePresence(session *discordgo.Session) { + values := reflect.ValueOf(session) + fieldWsConn := reflect.Indirect(values).FieldByName("wsConn") + fieldWsMutex := reflect.Indirect(values).FieldByName("wsMutex") + + wsConn := getUnexportedField(fieldWsConn).(*websocket.Conn) + wsMutex := getUnexportedField(fieldWsMutex).(sync.Mutex) + + afk := state.IsAFK() + presence := GatewayPresenceUpdate{ + Since: 0, + AFK: afk, + Activities: make([]Activity, 0), + } + + currentGuild := state.GetCurrentGuild() + currentChannel := state.GetCurrentChannel() + + var activity Activity + + startTime := state.GetStartTime() + + if session.State.User.Bot { + activity = Activity{ + Type: 0, + Name: "comcord", + } + + if currentGuild != "" && currentChannel != "" { + guild, guildErr := session.State.Guild(currentGuild) + channel, channelErr := session.State.Channel(currentChannel) + + if guildErr == nil && channelErr == nil { + activity.Type = 3 + activity.Name = fmt.Sprintf("#%s in %s | comcord", channel.Name, guild.Name) + } + } + + if afk { + activity.Name = activity.Name + " [AFK]" + } + } else { + activity = Activity{ + Type: 0, + ApplicationID: "1026163285877325874", + Name: "comcord", + Timestamps: discordgo.TimeStamps{ + StartTimestamp: startTime.Unix(), + }, + Buttons: make([]string, 0), + Metadata: ActivityMetadata{ + ButtonURLs: make([]string, 0), + }, + } + + activity.Buttons = append(activity.Buttons, "comcord Repo") + activity.Metadata.ButtonURLs = append(activity.Metadata.ButtonURLs, "https://gitdab.com/Cynosphere/comcord") + + if currentGuild != "" && currentChannel != "" { + guild, guildErr := session.State.Guild(currentGuild) + channel, channelErr := session.State.Channel(currentChannel) + + if guildErr == nil && channelErr == nil { + activity.Details = fmt.Sprintf("#%s - %s", channel.Name, guild.Name) + + activity.Assets = discordgo.Assets{} + activity.Assets.LargeText = guild.Name + if guild.Icon != "" { + activity.Assets.LargeImageID = fmt.Sprintf("mp:icons/%s/%s.png?size=1024", guild.ID, guild.Icon) + } + } + } + + if afk { + activity.State = "AFK" + } + } + + activity.CreatedAt = startTime + + presence.Activities = append(presence.Activities, activity) + + defaultStatus := state.GetConfigValue("defaultStatus") + if defaultStatus != "" { + presence.Status = defaultStatus + } else { + if afk { + presence.Status = "idle" + } else { + presence.Status = "online" + } + } + + op := presenceOp{3, presence} + wsMutex.Lock() + wsConn.WriteJSON(op) + wsMutex.Unlock() +} diff --git a/main.go b/main.go index 0b8bdde..12f6a58 100644 --- a/main.go +++ b/main.go @@ -90,6 +90,23 @@ func main() { } } + status := "online" + defaultStatus := config["defaultStatus"] + if defaultStatus != "" { + status = defaultStatus + } + startTime := state.GetStartTime() + client.Identify.Presence = discordgo.GatewayStatusUpdate{ + Since: 0, + Status: status, + AFK: false, + Game: discordgo.Activity{ + Name: "comcord", + ApplicationID: "1026163285877325874", + CreatedAt: startTime, + }, + } + events.Setup(client) err = client.Open() diff --git a/state/main.go b/state/main.go index cd63ec8..c46d09d 100644 --- a/state/main.go +++ b/state/main.go @@ -10,7 +10,7 @@ type ComcordState struct { Config map[string]string Connected bool RPCConnected bool - StartTime int64 + StartTime time.Time CurrentGuild string CurrentChannel string NameLength int @@ -30,7 +30,7 @@ func Setup(config map[string]string) { state.Config = config state.Connected = true state.RPCConnected = false - state.StartTime = time.Now().Unix() + state.StartTime = time.Now() state.CurrentGuild = "" state.CurrentChannel = "" state.NameLength = 2 @@ -59,7 +59,7 @@ func SetRPCConnected(value bool) { state.RPCConnected = value } -func GetStartTime() int64 { +func GetStartTime() time.Time { return state.StartTime }