Added recording to database of voice channel state changes.
Cleaned up messaging in terminal logs, and added new message for switching voice channels.
This commit is contained in:
parent
c257a8df75
commit
c967a03257
12
go.mod
12
go.mod
@ -4,8 +4,18 @@ go 1.24.1
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bwmarrin/discordgo v0.29.0 // indirect
|
github.com/bwmarrin/discordgo v0.29.0 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/gorilla/websocket v1.4.2 // indirect
|
github.com/gorilla/websocket v1.4.2 // indirect
|
||||||
github.com/joho/godotenv v1.5.1 // indirect
|
github.com/joho/godotenv v1.5.1 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||||
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
|
modernc.org/libc v1.65.10 // indirect
|
||||||
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
|
modernc.org/memory v1.11.0 // indirect
|
||||||
|
modernc.org/sqlite v1.38.0 // indirect
|
||||||
)
|
)
|
||||||
|
23
go.sum
23
go.sum
@ -1,14 +1,37 @@
|
|||||||
github.com/bwmarrin/discordgo v0.29.0 h1:FmWeXFaKUwrcL3Cx65c20bTRW+vOb6k8AnaP+EgjDno=
|
github.com/bwmarrin/discordgo v0.29.0 h1:FmWeXFaKUwrcL3Cx65c20bTRW+vOb6k8AnaP+EgjDno=
|
||||||
github.com/bwmarrin/discordgo v0.29.0/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
github.com/bwmarrin/discordgo v0.29.0/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
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/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||||
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
modernc.org/libc v1.65.10 h1:ZwEk8+jhW7qBjHIT+wd0d9VjitRyQef9BnzlzGwMODc=
|
||||||
|
modernc.org/libc v1.65.10/go.mod h1:StFvYpx7i/mXtBAfVOjaU0PWZOvIRoZSgXhrwXzr8Po=
|
||||||
|
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||||
|
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||||
|
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||||
|
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||||
|
modernc.org/sqlite v1.38.0 h1:+4OrfPQ8pxHKuWG4md1JpR/EYAh3Md7TdejuuzE7EUI=
|
||||||
|
modernc.org/sqlite v1.38.0/go.mod h1:1Bj+yES4SVvBZ4cBOpVZ6QgesMCKpJZDq0nxYzOpmNE=
|
||||||
|
127
main.go
127
main.go
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@ -12,6 +13,8 @@ import (
|
|||||||
|
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
|
|
||||||
|
_ "modernc.org/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
var botToken string
|
var botToken string
|
||||||
@ -19,9 +22,11 @@ var announceChannelName string
|
|||||||
var cleanDelay = 30
|
var cleanDelay = 30
|
||||||
|
|
||||||
var confFile string
|
var confFile string
|
||||||
|
var dbFile string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.StringVar(&confFile, "conf", ".env", ".env file w/ config variables")
|
flag.StringVar(&confFile, "conf", ".env", ".env file w/ config variables")
|
||||||
|
flag.StringVar(&dbFile, "db", "data.db", "db to store logs of events in")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,11 +38,51 @@ var joinMessages = []string{
|
|||||||
"<@%s> is hanging out in <#%s>",
|
"<@%s> is hanging out in <#%s>",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Channel struct {
|
||||||
|
GuildID, ChannelID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
*sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(dbFile string) (*Server, error) {
|
||||||
|
db, err := sql.Open("sqlite", dbFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to open db %q: %w", dbFile, err)
|
||||||
|
}
|
||||||
|
tx, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to work on db %q: %w", dbFile, err)
|
||||||
|
}
|
||||||
|
tx.Exec(`CREATE TABLE IF NOT EXISTS voice_stats (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
guildID INTEGER,
|
||||||
|
guildName TEXT,
|
||||||
|
memberId INTEGER,
|
||||||
|
memberName TEXT,
|
||||||
|
channelID INTEGER,
|
||||||
|
channelName TEXT,
|
||||||
|
action TEXT,
|
||||||
|
timestamp TIMESTAMP
|
||||||
|
);`)
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create table: %w", err)
|
||||||
|
}
|
||||||
|
s := Server{DB: db}
|
||||||
|
return &s, nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
envs, err := godotenv.Read(confFile)
|
envs, err := godotenv.Read(confFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Unable to get environment variables: %v", err)
|
log.Fatalf("Unable to get environment variables: %v", err)
|
||||||
}
|
}
|
||||||
|
s, err := NewServer(dbFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("can't create server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// set values from env
|
// set values from env
|
||||||
botToken = envs["BOT_TOKEN"]
|
botToken = envs["BOT_TOKEN"]
|
||||||
@ -48,41 +93,88 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := discordgo.New("Bot " + botToken)
|
ds, err := discordgo.New("Bot " + botToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log all of the servers tht this bot is installed in.
|
// Log all of the servers tht this bot is installed in.
|
||||||
s.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) {
|
ds.AddHandler(func(ds *discordgo.Session, r *discordgo.Ready) {
|
||||||
for _, g := range r.Guilds {
|
for _, g := range r.Guilds {
|
||||||
g2, _ := s.Guild(g.ID)
|
g2, _ := ds.Guild(g.ID)
|
||||||
log.Printf("Bot is connected to %q.", g2.Name)
|
log.Printf("Bot is connected to %q.", g2.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add different capability handlers:
|
// Add different capability handlers:
|
||||||
s.AddHandler(voiceStatus)
|
ds.AddHandler(s.voiceStatus)
|
||||||
|
|
||||||
err = s.Open()
|
err = ds.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Cannot open the session: %v", err)
|
log.Fatalf("Cannot open the session: %v", err)
|
||||||
}
|
}
|
||||||
defer s.Close()
|
defer ds.Close()
|
||||||
|
|
||||||
stop := make(chan os.Signal, 1)
|
stop := make(chan os.Signal, 1)
|
||||||
signal.Notify(stop, os.Interrupt)
|
signal.Notify(stop, os.Interrupt)
|
||||||
<-stop
|
<-stop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VoiceState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Joining VoiceState = iota
|
||||||
|
Switching
|
||||||
|
Leaving
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s VoiceState) String() string {
|
||||||
|
return []string{"Joined", "Switched", "Left"}[s]
|
||||||
|
}
|
||||||
|
|
||||||
// Handle for when someone joins a voice channel - send notification about them joining.
|
// Handle for when someone joins a voice channel - send notification about them joining.
|
||||||
func voiceStatus(s *discordgo.Session, m *discordgo.VoiceStateUpdate) {
|
func (s *Server) voiceStatus(ds *discordgo.Session, m *discordgo.VoiceStateUpdate) {
|
||||||
if m.BeforeUpdate != nil || m.VoiceState == nil {
|
var state VoiceState
|
||||||
|
switch {
|
||||||
|
case len(m.ChannelID) == 0:
|
||||||
|
state = Leaving
|
||||||
|
case m.BeforeUpdate != nil:
|
||||||
|
state = Switching
|
||||||
|
}
|
||||||
|
|
||||||
|
guild, _ := ds.Guild(m.VoiceState.GuildID)
|
||||||
|
|
||||||
|
switch state {
|
||||||
|
case Leaving, Switching:
|
||||||
|
channel, _ := ds.Channel(m.BeforeUpdate.ChannelID)
|
||||||
|
log.Default().Printf("%q has left %q in %q", m.Member.DisplayName(), channel.Name, guild.Name)
|
||||||
|
|
||||||
|
_, err := s.Exec("INSERT INTO voice_stats (guildID, guildName, memberID, memberName, channelID, channelName, action, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", guild.ID, guild.Name, m.VoiceState.UserID, m.VoiceState.Member.DisplayName(), channel.ID, channel.Name, "LEFT", time.Now())
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("unable to save log to db: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch state {
|
||||||
|
case Joining, Switching:
|
||||||
|
channel, _ := ds.Channel(m.ChannelID)
|
||||||
|
log.Default().Printf("%q has joined %q in %q", m.Member.DisplayName(), channel.Name, guild.Name)
|
||||||
|
|
||||||
|
_, err := s.Exec("INSERT INTO voice_stats (guildID, guildName, memberID, memberName, channelID, channelName, action, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", guild.ID, guild.Name, m.VoiceState.UserID, m.VoiceState.Member.DisplayName(), channel.ID, channel.Name, "JOIN", time.Now())
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("unable to save log to db: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch state {
|
||||||
|
case Leaving:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
channels, err := s.GuildChannels(m.VoiceState.GuildID)
|
|
||||||
|
channels, err := ds.GuildChannels(m.GuildID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Unable to get channels for Guild: %s", m.VoiceState.GuildID)
|
log.Printf("Unable to get channels for Guild: %s", m.GuildID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var announceChannel *discordgo.Channel
|
var announceChannel *discordgo.Channel
|
||||||
@ -92,18 +184,21 @@ func voiceStatus(s *discordgo.Session, m *discordgo.VoiceStateUpdate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if announceChannel == nil {
|
if announceChannel == nil {
|
||||||
log.Printf("Unable to get announce channel for Guild: %s", m.VoiceState.GuildID)
|
log.Printf("Unable to get announce channel for Guild: %s", m.GuildID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
msg := joinMessages[rand.IntN(len(joinMessages))]
|
msg := fmt.Sprintf(joinMessages[rand.IntN(len(joinMessages))], m.UserID, m.ChannelID)
|
||||||
|
switch state {
|
||||||
|
case Switching:
|
||||||
|
msg = fmt.Sprintf("<@%s> has left <#%s> to join <#%s>", m.UserID, m.BeforeUpdate.ChannelID, m.ChannelID)
|
||||||
|
}
|
||||||
|
|
||||||
sent, err := s.ChannelMessageSend(announceChannel.ID, fmt.Sprintf(msg, m.VoiceState.UserID, m.VoiceState.ChannelID))
|
sent, err := ds.ChannelMessageSend(announceChannel.ID, msg)
|
||||||
g, _ := s.Guild(m.VoiceState.GuildID)
|
|
||||||
log.Default().Printf("%q has joined %q in %s", m.VoiceState.Member.DisplayName(), m.VoiceState.ChannelID, g.Name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unable to send message: %s", err)
|
log.Printf("unable to send message: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
time.AfterFunc(time.Second*time.Duration(cleanDelay), func() {
|
time.AfterFunc(time.Second*time.Duration(cleanDelay), func() {
|
||||||
s.ChannelMessageDelete(sent.ChannelID, sent.ID)
|
ds.ChannelMessageDelete(sent.ChannelID, sent.ID)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user