comcord/lib/presence.go

160 lines
4.8 KiB
Go

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) {
// there is a way to send presence without reflecting to grab the websocket
// connection, but theres an issue with the serialization that because a value
// isn't being considered "null" that its trying to apply and failing because
// the default doesn't make sense in this context, even if omitempty is set
//
// this doesnt happen with bot accounts because they have certain fields
// stripped
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()
}