Skip to content
Snippets Groups Projects
Commit c2620d37 authored by Levatax's avatar Levatax
Browse files

implement chat instance handling, implement channel creation handler,...

implement chat instance handling, implement channel creation handler, implement channel member tracking handler and auto channel destructor, improve configuration lookup
parent a15ae23d
Branches
No related tags found
No related merge requests found
......@@ -6,8 +6,19 @@ import (
)
func cleanup() {
// Close all chatInstance
instances := make(map[string]*chatInstance)
for key, value := range instancesChannel {
instances[key] = value
}
for _, instance := range instances {
instance.destroy()
}
// Close session
if err := session.Close(); err != nil {
log.Fatalf("Error while closing session, %s", err)
log.Fatalf("Error closing session, %s", err)
os.Exit(1)
}
}
......@@ -10,15 +10,17 @@ import (
var config configPayload
var configPath string
var defaultConfig = configPayload{
Prefix: "!",
Token: "TOKEN",
Timeout: 120,
ChannelID: []string{},
Prefix: "!",
}
type configPayload struct {
Prefix string
Token string
Timeout int
ChannelID []string
Prefix string
}
func init() {
......@@ -45,9 +47,32 @@ func parse() {
} else {
log.Infof("Loaded config at %s.", configPath)
}
}
func configLate() {
allowedChannels = make(map[string]bool)
categoryMap = make(map[string]string)
guildMap = make(map[string]string)
for _, id := range config.ChannelID {
if channel, err := session.Channel(id); err != nil {
log.Errorf("Error getting channel %s, %s", id, err)
continue
} else {
if channel.ParentID == "" {
log.Warnf("Channel %s has no parent.", id)
continue
} else {
if guildMap[channel.GuildID] != "" {
log.Warnf("Multiple channels specified for guild %s.", channel.GuildID)
continue
} else {
log.Infof("Channel %s from guild %s with parent %s added.",
channel.ID, channel.GuildID, channel.ParentID)
guildMap[channel.GuildID] = channel.ID
categoryMap[channel.ID] = channel.ParentID
}
}
}
allowedChannels[id] = true
}
}
......@@ -7,28 +7,39 @@ import (
)
func init() {
m.VoiceStateUpdate = append(m.VoiceStateUpdate, handleVoiceUpdate)
m.VoiceStateUpdate = append(m.VoiceStateUpdate, handleChatInitiate)
m.VoiceStateUpdate = append(m.VoiceStateUpdate, handleInstanceChannelActivity)
}
var allowedChannels map[string]bool
func handleVoiceUpdate(context *multiplexer.Context) {
if context.Channel == nil {
return
}
if !allowedChannels[context.Channel.ID] {
func handleChatInitiate(context *multiplexer.Context) {
// Lookup channel in allowed map
if context.Channel == nil || !allowedChannels[context.Channel.ID] {
return
}
// Get event
var event *discordgo.VoiceStateUpdate
if e, ok := context.Event.(*discordgo.VoiceStateUpdate); !ok {
return
} else {
event = e
}
if event.VoiceState != nil {
if event.VoiceState.ChannelID == "" {
return
}
} else {
return
}
// Lookup already existing channel
if instancesUser[event.UserID] != nil {
return
}
// Get member from cache and add to cache if not exist
var member *discordgo.Member
if u, err := context.Session.State.Member(event.GuildID, event.UserID); err != nil {
if u, err = context.Session.GuildMember(event.GuildID, event.UserID); err != nil {
......@@ -41,16 +52,72 @@ func handleVoiceUpdate(context *multiplexer.Context) {
} else {
member = u
}
if member == nil {
return
}
log.Infof("%s#%s (%s) has joined the designated channel #%s (%s).",
// Create new instance
instance := newInstance(member)
if instance == nil {
return
}
log.Infof("%s#%s (%s) has created a new volatile channel #%s (%s) in guild %s (%s).",
member.User.Username,
member.User.Discriminator,
member.User.ID,
context.Channel.Name,
context.Channel.ID,
instance.Channel.Name,
instance.Channel.ID,
context.Guild.Name,
context.Guild.ID,
)
// Move user to newly created volatile channel
if err := session.GuildMemberMove(event.GuildID, event.UserID, &instance.Channel.ID); err != nil {
log.Errorf("Error moving user %s#%s (%s) to newly created volatile channel #%s (%s), %s",
member.User.Username,
member.User.Discriminator,
member.User.ID,
instance.Channel.Name,
instance.Channel.ID,
err)
}
}
func handleInstanceChannelActivity(context *multiplexer.Context) {
// Get event
var event *discordgo.VoiceStateUpdate
if e, ok := context.Event.(*discordgo.VoiceStateUpdate); !ok {
return
} else {
event = e
}
// Lookup channel in pre-event state
if event.BeforeUpdate == nil || instancesChannel[event.BeforeUpdate.ChannelID] == nil {
// Add member if join
instance := instancesChannel[event.ChannelID]
if instance != nil {
instance.Members[event.UserID] = true
}
return
}
// If both before update and current state are same and registered, do not handle
if event.BeforeUpdate.ChannelID == event.ChannelID && instancesChannel[event.ChannelID] != nil {
return
}
// Remove user from previous channel and destroy if zero length
instanceOld := instancesChannel[event.BeforeUpdate.ChannelID]
delete(instanceOld.Members, event.UserID)
if len(instanceOld.Members) == 0 {
instanceOld.destroy()
}
// Record user in new channel if registered
instance := instancesChannel[event.ChannelID]
if instance != nil {
instance.Members[event.UserID] = true
}
}
package main
import (
"git.randomchars.net/freenitori/log"
"github.com/bwmarrin/discordgo"
"time"
)
var (
categoryMap map[string]string
guildMap map[string]string
timeout time.Duration
)
type chatInstance struct {
Initiator *discordgo.Member
Channel *discordgo.Channel
PreviousAction time.Time
Members map[string]bool
}
var (
instancesChannel = make(map[string]*chatInstance)
instancesUser = make(map[string]*chatInstance)
)
// destroy destroys the chatInstance and removes the corresponding Discord voice channel.
func (c *chatInstance) destroy() {
delete(instancesChannel, c.Channel.ID)
delete(instancesUser, c.Initiator.User.ID)
if _, err := session.ChannelDelete(c.Channel.ID); err != nil {
log.Errorf("Error destroying channel %s initiated by user %s in guild %s, %s",
c.Channel.ID, c.Initiator.User.ID, c.Initiator.GuildID, err)
}
log.Infof("Chat instance with channel %s initiated by user %s#%s (%s) destroyed.",
c.Channel.ID, c.Initiator.User.Username, c.Initiator.User.Discriminator, c.Initiator.User.ID)
}
// actionFree checks if actions are free in chatInstance.
func (c *chatInstance) actionFree() bool {
return c.PreviousAction.Add(timeout).Before(time.Now())
}
// newInstance creates a new chatInstance on behalf of a discordgo.Member.
func newInstance(member *discordgo.Member) *chatInstance {
instance := setupInstance(member)
if instance != nil {
instancesChannel[instance.Channel.ID] = instance
instancesUser[instance.Initiator.User.ID] = instance
return instance
}
return nil
}
// setupInstance sets up a new chatInstance on behalf of a discordgo.Member.
func setupInstance(member *discordgo.Member) *chatInstance {
channelID := guildMap[member.GuildID]
if channelID == "" {
return nil
}
parentID := categoryMap[channelID]
if parentID == "" {
return nil
}
channel, err := session.GuildChannelCreateComplex(member.GuildID, discordgo.GuildChannelCreateData{
Name: member.User.Username + "#" + member.User.Discriminator,
Type: discordgo.ChannelTypeGuildVoice,
Topic: "Volatile channel created by " + member.User.ID,
Bitrate: 64000,
UserLimit: 10,
ParentID: parentID,
})
if err != nil {
log.Errorf("Error creating channel on guild %s on behalf of member %s, %s",
member.GuildID, member.User.ID, err)
return nil
}
return &chatInstance{
Initiator: member,
Channel: channel,
PreviousAction: time.Unix(0, 0),
Members: make(map[string]bool),
}
}
......@@ -51,13 +51,26 @@ func main() {
session.UserAgent = "DiscordBot (voice-bot)"
session.Token = "Bot " + config.Token
session.ShouldReconnectOnError = true
session.Identify.Intents = discordgo.IntentsAllWithoutPrivileged
session.Identify.Intents = discordgo.IntentsAll
session.State.TrackVoice = true
// Open session
func() {
open:
if err := session.Open(); err != nil {
if session.Identify.Intents == discordgo.IntentsAll {
log.Warnf("Wasn't able to start with full intents, some stuff might not work (%s)", err)
session.Identify.Intents = discordgo.IntentsAllWithoutPrivileged
goto open
}
log.Fatalf("Error while opening session, %s", err)
os.Exit(1)
}
}()
// Execute late config options
configLate()
// Setup multiplexer
m.SessionRegisterHandlers(session)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment