Skip to content
Lukas SP edited this page Sep 1, 2019 · 49 revisions

Contents

HTTP 401 Errors

HTTP 401 Unauthorized, {"code": 0, "message": "401: Unauthorized"}

You may be getting a 401 error if you forgot to prefix your token with Bot . Discord requires that you do so to authenticate with a bot user.

example:

discordgo.New(`Bot 123456789012345627`)

Sending embeds

Embeds are sent through the ChannelMessageSendEmbed method. To do so, you will need to create an embed.

Creating an embed

embed := &discordgo.MessageEmbed{
    Author:      &discordgo.MessageEmbedAuthor{},
    Color:       0x00ff00, // Green
    Description: "This is a discordgo embed",
    Fields: []*discordgo.MessageEmbedField{
        &discordgo.MessageEmbedField{
            Name:   "I am a field",
            Value:  "I am a value",
            Inline: true,
        },
        &discordgo.MessageEmbedField{
            Name:   "I am a second field",
            Value:  "I am a value",
            Inline: true,
        },
    },
    Image: &discordgo.MessageEmbedImage{
        URL: "https://cdn.discordapp.com/avatars/119249192806776836/cc32c5c3ee602e1fe252f9f595f9010e.jpg?size=2048",
    },
    Thumbnail: &discordgo.MessageEmbedThumbnail{
        URL: "https://cdn.discordapp.com/avatars/119249192806776836/cc32c5c3ee602e1fe252f9f595f9010e.jpg?size=2048",
    },
    Timestamp: time.Now().Format(time.RFC3339), // Discord wants ISO8601; RFC3339 is an extension of ISO8601 and should be completely compatible.
    Title:     "I am an Embed",
}

session.ChannelMessageSendEmbed(channelid, embed)

Simplifying Embeds

Because creating an embed is a lot of typing, you could simplify it by creating a struct that anonymously embeds the *discordgo.MessageEmbed and use that to create helper functions.

Here is an example, which copies discord.js's helper functions for embeds. Using this, the previous method of creating an embed would become

embed := NewEmbed().
    SetTitle("I am an embed").
    SetDescription("This is a discordgo embed").
    AddField("I am a field", "I am a value").
    AddField("I am a second field", "I am a value").
    SetImage("https://cdn.discordapp.com/avatars/119249192806776836/cc32c5c3ee602e1fe252f9f595f9010e.jpg?size=2048").
    SetThumbnail("https://cdn.discordapp.com/avatars/119249192806776836/cc32c5c3ee602e1fe252f9f595f9010e.jpg?size=2048").
    SetColor(0x00ff00).MessageEmbed


session.ChannelMessageSendEmbed(channelid, embed)

Embeds using attachments

Attachments can be used to send files along with embed messages.

f, err := os.Open(fileName)
if err != nil {
    return nil, err
}
defer f.Close()

ms := &discordgo.MessageSend{
    Embed: &discordgo.MessageEmbed{
        Image: &discordgo.MessageEmbedImage{
            URL: "attachment://" + fileName,
        },
    },
    Files: []*discordgo.File{
        &discordgo.File{
            Name:   fileName,
            Reader: f,
        },
    },
}

s.ChannelMessageSendComplex(channelID, ms)

Permissions and Roles

Determining if a role has a permission

Discord permissions are stored as an integer. To extract the permission from the role you need to use the bitwise AND (&) operator and see if the result is not zero.

If you are checking permissions specific to channels, make sure to remember the channel PermissionOverides which can either allow or deny a permission to a role, this is done for you with UserChannelPermissions

// MemberHasPermission checks if a member has the given permission
// for example, If you would like to check if user has the administrator
// permission you would use
// --- MemberHasPermission(s, guildID, userID, discordgo.PermissionAdministrator)
// If you want to check for multiple permissions you would use the bitwise OR
// operator to pack more bits in. (e.g): PermissionAdministrator|PermissionAddReactions
// =================================================================================
//     s          :  discordgo session
//     guildID    :  guildID of the member you wish to check the roles of
//     userID     :  userID of the member you wish to retrieve
//     permission :  the permission you wish to check for
func MemberHasPermission(s *discordgo.Session, guildID string, userID string, permission int) (bool, error) {
	member, err := s.State.Member(guildID, userID)
	if err != nil {
		if member, err = s.GuildMember(guildID, userID); err != nil {
			return false, err
		}
	}

    // Iterate through the role IDs stored in member.Roles
    // to check permissions
	for _, roleID := range member.Roles {
		role, err := s.State.Role(guildID, roleID)
		if err != nil {
			return false, err
		}
		if role.Permissions&permission != 0 {
			return true, nil
		}
	}

	return false, nil
}

Get the time a snowflake was created

A snowflake is what discord uses for its ID system, (e.g) user.ID, channel.ID, guild.ID.

// CreationTime returns the creation time of a Snowflake ID relative to the creation of Discord.
// Taken from https://github.com/Moonlington/FloSelfbot/blob/master/commands/commandutils.go#L117
func CreationTime(ID string) (t time.Time, err error) {
	i, err := strconv.ParseInt(ID, 10, 64)
	if err != nil {
		return
	}
	timestamp := (i >> 22) + 1420070400000
	t = time.Unix(timestamp/1000, 0)
	return
}

Getting the guild from a message

To get a guild from a message you first need to obtain the guildID from the channel. then get the guild from the guildID property of the channel.

Getting the channel and guild from the state is optional.

NOTE: the old method of getting a guild from a message required first getting the channel to get the GuildID and then retrieving the guild from there. Now that the guildID is stored in the message struct, as well as many others, this is no longer needed.

// Attempt to get the guild from the state, 
// If there is an error, fall back to the restapi.
guild, err := session.State.Guild(message.GuildID)
if err != nil {
    guild, err = session.Guild(message.GuildID)
    if err != nil {
        return
    }
}

Important note about guild members

I want to let you know that the Session.GuildMember() function does not fill in the GuildID field of the Member struct. Session.State.Member() does fill it. Because I know some people who did have some issues with that fact, I decided to create a little FAQ entry.

Checking if a message is a direct message (DM)

// ComesFromDM returns true if a message comes from a DM channel
func ComesFromDM(s *discordgo.Session, m *discordgo.MessageCreate) (bool, error) {
	channel, err := s.State.Channel(m.ChannelID)
	if err != nil {
		if channel, err = s.Channel(m.ChannelID); err != nil {
			return false, err
		}
	}

	return channel.Type == discordgo.ChannelTypeDM, nil
}

Getting multiple messages from a channel

Only bots have access to the ChannelMessages function. If you are using a user account, you will have to fetch the messages through the state.

Bot account

example:

// This will fetch a limit of 100 messages from channelID
// The beforeID, aroundID, and afterID are optional parameters so they can 
// Be left blank
messages, _ := session.ChannelMessages(channelID, 100, "", "", "")

User account

To utilize the message caching feature of the state, you have to set State.MaxMessageLimit to a value greater than zero.

example:

// Create a discordgo session and set the state message tracking limit
session, _ := discordgo.New("Bot token")
session.State.MaxMessageLimit = 100;


// Obtain messages from channel
channel, _ := session.State.Channel(channelID)
for i := 0; i < 100; i++ {
    fmt.Println(channel.Messages[i].Content)
}

Playing audio over a voice connection

Finding a user's voice channel

If you want to find the voice channel a user is currently in, you need to search for their voice state in the State. This example scans over every guild's voice states to find the User's.

func findUserVoiceState(session *discordgo.Session, userid string) (*discordgo.VoiceState, error) {
	for _, guild := range session.State.Guilds {
		for _, vs := range guild.VoiceStates {
			if vs.UserID == userid {
				return vs, nil
			}
		}
	}
	return nil, errors.New("Could not find user's voice state")
}

If you only want to scan a single guild, you can obtain a guild from state using State.Guild and scan for the userID in the voice states.

Connecting to a voice channel

func joinUserVoiceChannel(session *discordgo.Session, userID string) (*discordgo.VoiceConnection, error) {
	// Find a user's current voice channel
	vs, err := findUserVoiceState(session, userID)
	if err != nil {
		return nil, err
	}

    // Join the user's channel and start unmuted and deafened.
    return session.ChannelVoiceJoin(vs.GuildID, vs.ChannelID, false, true)
}

Reading opus

// Reads an opus packet to send over the vc.OpusSend channel
func readOpus(source io.Reader) ([]byte, error) {
	var opuslen int16
	err := binary.Read(source, binary.LittleEndian, &opuslen)
	if err != nil {
		if err == io.EOF || err == io.ErrUnexpectedEOF {
			return nil, err
		}
		return nil, errors.New("ERR reading opus header")
	}

	var opusframe = make([]byte, opuslen)
	err = binary.Read(source, binary.LittleEndian, &opusframe)
	if err != nil {
		if err == io.EOF || err == io.ErrUnexpectedEOF {
			return nil, err
		}
		return nil, errors.New("ERR reading opus frame")
	}

	return opusframe, nil
}

Sending opus

Follow the instructions here.

Clone this wiki locally