Compare commits

...

3 commits

4 changed files with 207 additions and 22 deletions

View file

@ -442,7 +442,7 @@ func SwitchGuild(session *discordgo.Session, input string) {
ListChannelsCommand(session) ListChannelsCommand(session)
ListUsersCommand(session) ListUsersCommand(session)
// TODO: update presence lib.UpdatePresence(session)
} }
} }
} }
@ -486,7 +486,7 @@ func SwitchChannelsCommand(session *discordgo.Session) {
ListUsersCommand(session) ListUsersCommand(session)
// TODO: update presence lib.UpdatePresence(session)
} }
} }
}) })

159
lib/presence.go Normal file
View file

@ -0,0 +1,159 @@
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()
}

56
main.go
View file

@ -63,8 +63,15 @@ func main() {
state.Setup(config) state.Setup(config)
commands.Setup() commands.Setup()
// TODO: user account support allowUserAccounts := config["allowUserAccounts"] == "true"
client, err := discordgo.New("Bot " + token) tokenPrefix := "Bot "
if allowUserAccounts {
tokenPrefix = ""
}
fullToken := tokenPrefix + token
client, err := discordgo.New(fullToken)
if err != nil { if err != nil {
fmt.Println("% Failed to create client:", err) fmt.Println("% Failed to create client:", err)
fmt.Print("\r") fmt.Print("\r")
@ -72,22 +79,41 @@ func main() {
return return
} }
// TODO: dont set for user accounts(? never really tested if it matters) //client.LogLevel = discordgo.LogDebug
client.Identify.Intents = discordgo.IntentsAll client.Identify.Intents = discordgo.IntentsAll
if config["useMobile"] == "true" { client.Identify.Properties = discordgo.IdentifyProperties{
client.Identify.Properties = discordgo.IdentifyProperties{ OS: runtime.GOOS,
OS: "Android", }
Browser: "Discord Android", statusType := config["statusType"]
Device: "Pixel, raven", if statusType == "mobile" {
} client.Identify.Properties.Browser = "Discord Android"
} else if statusType == "embedded" {
client.Identify.Properties.Browser = "Discord Embedded"
} else if statusType == "desktop" {
client.Identify.Properties.Browser = "Discord Client"
} else { } else {
// TODO: user account support client.Identify.Properties.Browser = "comcord"
client.Identify.Properties = discordgo.IdentifyProperties{ }
OS: runtime.GOOS,
Browser: "comcord", status := "online"
Device: "comcord", defaultStatus := config["defaultStatus"]
} if defaultStatus != "" {
status = defaultStatus
}
startTime := state.GetStartTime()
client.Identify.Presence = discordgo.GatewayStatusUpdate{
Since: 0,
Status: status,
AFK: false,
Game: discordgo.Activity{
Type: 0,
Name: "comcord",
ApplicationID: "1026163285877325874",
CreatedAt: startTime,
},
} }
events.Setup(client) events.Setup(client)

View file

@ -1,16 +1,16 @@
package state package state
import ( import (
"time" "time"
"github.com/bwmarrin/discordgo" "github.com/bwmarrin/discordgo"
) )
type ComcordState struct { type ComcordState struct {
Config map[string]string Config map[string]string
Connected bool Connected bool
RPCConnected bool RPCConnected bool
StartTime int64 StartTime time.Time
CurrentGuild string CurrentGuild string
CurrentChannel string CurrentChannel string
NameLength int NameLength int
@ -30,7 +30,7 @@ func Setup(config map[string]string) {
state.Config = config state.Config = config
state.Connected = true state.Connected = true
state.RPCConnected = false state.RPCConnected = false
state.StartTime = time.Now().Unix() state.StartTime = time.Now()
state.CurrentGuild = "" state.CurrentGuild = ""
state.CurrentChannel = "" state.CurrentChannel = ""
state.NameLength = 2 state.NameLength = 2
@ -59,7 +59,7 @@ func SetRPCConnected(value bool) {
state.RPCConnected = value state.RPCConnected = value
} }
func GetStartTime() int64 { func GetStartTime() time.Time {
return state.StartTime return state.StartTime
} }