Harmony is a peaceful Go module for interacting with Discord's API

GoDoc License MIT Discord Build Status

Harmony

Harmony is a peaceful Go module for interacting with Discord's API.

Although this package is usable, it still is under active development so please don't use it for anything other than experiments, yet.

Contents

Installation

Make sure you have a working Go installation, if not see this page first.

Then, install this package with the go get command:

go get github.com/skwair/harmony

Note that go get will always pull the latest version from the master branch before Go 1.11. With newer versions and Go modules enabled, the latest minor or patch release will be downloaded. go get github.com/skwair/[email protected] can be used to download a specific version. See Go modules for more information.

Usage

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/skwair/harmony"
)

func main() {
    client, err := harmony.NewClient("your.bot.token")
    if err != nil {
        log.Fatal(err)
    }

    // Get information about the current user (the bot itself).
    u, err := client.User("@me").Get(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(u)
}

For information about how to create bots and more examples on how to use this package, check out the examples directory and the tests.

Testing

For now, only some end to end tests are provided with this module. To run them, you will need a valid bot token and a valid Discord server ID. The bot attached to the token must be in the server with administrator permissions.

  1. Create a Discord test server

From a Discord client and with you main account, simply create a new server. Then, right click on the new server and get its ID.

Note that for the UI to have the Copy ID option when right clicking on the server, you will need to enable developer mode. You can find this option in User settings > Appearance > Advanced > Developer Mode.

  1. Create a bot and add it to the test Discord server

Create a bot (or use an existing one) and add it to the freshly created server.

See the example directory for information on how to create a bot and add it to a server.

  1. Set required environment variables and run the tests

Set HARMONY_TEST_BOT_TOKEN to the token of your bot and HARMONY_TEST_GUILD_ID to the ID of the server you created and simply run:

⚠️ For the tests to be reproducible, they will start by deleting ALL channels in the provided server. Please make sure to provide a server created ONLY for those tests. ⚠️

go test -v -race ./...

Step 1 and 2 must be done only once for initial setup. Once you have your bot token and the ID of your test server, you can run the tests as many times as you want.

How does it compare to DiscordGo?

Harmony exposes its API differently. It uses a resource-based approach which organizes methods by topic, greatly reducing the number of methods on the main Client type. The goal by doing this is to have a more friendly API which is easier to navigate.

Another key difference is in the "event handler" mechanism. Instead of having a single method that takes an interface{} as a parameter and guesses which event you registered a handler for based on its concrete type, this library provides a dedicated method for each event type, making it clear what signature your handler must have and ensuring it at compile time, not at runtime.

Each action that results in an entry in the audit log has a ...WithReason form, allowing to set a reason for the change (see the X-Audit-Log-Reason header documentation for more information).

Finally, this library has a full support of the context package, allowing the use of timeouts, deadlines and cancellation when interacting with Discord's API.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Original logo by Renee French, dressed with the cool t-shirt by @HlneChd.

Comments
  • fix discord.Time's UnmarshalJSON implementation

    fix discord.Time's UnmarshalJSON implementation

    Hey there,

    I ran into a parsing error when registering a handler for PRESENCE_UPDATE; specifically within the CreatedAt field of the handled discord.Presence's Game field. After some trial and error I realized it's due to the quotation marks within the bytes passed to the UnmarshalJSON implementation. I tested the fix I've got attached here, if there's anything I need to change do let me know; this is my first Go PR so I appreciate you bearing with me.

    Thanks

  • Client fails to properly disconnect when Gateway sends reconnect payload

    Client fails to properly disconnect when Gateway sends reconnect payload

    Description

    When receiving an opcode 7 reconnect payload from the Gateway, Harmony fails to disconnect, and thus to reconnect after.

    Initially reported by 0x5444 on Discord, thanks!

    What did you do?

    Let a bot run until it receives the before-mentioned payload.

    What did you expect to see?

    The bot should reconnect successfully.

    What did you see instead?

    With debug enabled, these are the last logs printed:

    ...
    2020/12/01 17:55:01 [DEBUG] sent payload: {code: 1, data: 9}
    2020/12/01 17:55:01 [DEBUG] received payload: {code: 11}
    2020/12/01 17:55:21 [DEBUG] received payload: {code: 7}
    2020/12/01 17:55:21 [DEBUG] stopped gateway heartbeater
    2020/12/01 17:55:21 [DEBUG] disconnecting from the gateway
    2020/12/01 17:55:21 [DEBUG] stopped gateway connection manager
    

    The payload listener does not seem to stop properly so it's very likely that this bug comes from this function: https://github.com/skwair/harmony/blob/40b27cf8c1ac3d3c113346b9d5af6cf865605a95/internal/payload/listen.go#L19-L43

    What version of the module are you using ?

    v0.17.0

  • Can't leave correctly from voice channel

    Can't leave correctly from voice channel

    Description Joining to a voice channel always works well, but leaving does not always work correctly. Sometimes works fine, but not always.

    What did you do? I just try to connect and then disconnect from a specific voice channel. Here is the message handler

    if m.Content == "!connect" {
    	mu.Lock()
    	defer mu.Unlock()
    
    	_, err := b.client.JoinVoiceChannel(context.TODO(), m.GuildID, channelID, false, false)
    	if err != nil {
    		fmt.Println("Error", err)
    		return
    	}
    } else if m.Content == "!disconnect" {
    	mu.Lock()
    	defer mu.Unlock()
    	b.client.LeaveVoiceChannel(context.TODO(), m.GuildID)
    }
    

    What did you expect to see? I expected to always disconnect correctly.

    What did you see instead? I'm using the default logger. This is the output:

    [DEBUG] disconnecting from the voice server
    [DEBUG] stopped Opus sender
    [DEBUG] stopped UDP heartbeater
    [DEBUG] stopped voice connection heartbeater
    [DEBUG] stopped Opus receiver
    [DEBUG] stopped voice connection event listener
    [DEBUG] stopped voice connection manager
    [DEBUG] sent payload: {code: 1, data: 8}
    [DEBUG] received payload: {code: 11}
    [DEBUG] sent payload: {code: 1, data: 8}
    [DEBUG] received payload: {code: 11}
    [DEBUG] sent payload: {code: 1, data: 8}
    [DEBUG] received payload: {code: 11}
    [DEBUG] sent payload: {code: 1, data: 8}
    [DEBUG] received payload: {code: 11}
    [DEBUG] sent payload: {code: 1, data: 8}
    [DEBUG] received payload: {code: 11}
    

    and never disconnects

    What version of the module are you using ? I'm using v0.14.0

  • Add warning when trying to register an event handler with missing intent

    Add warning when trying to register an event handler with missing intent

    Description

    This PR adds a warning log if an event handler is registered but it doesn't have the matching intent. While it's not an error to do so strictly speaking, it is very likely an oversight.

  • Upgrade to Gateway v8

    Upgrade to Gateway v8

    Description

    This PR upgrades Harmony to use Discord's latest Gateway version, v8. It also ships with a fix to the old Client.SetStatus method, now renamed Cient.SetBotStatus.

  • No method exists to remove a guild member role with a reason

    No method exists to remove a guild member role with a reason

    Description No method exists to remove a role from a guild member while also providing a reason, similar to harmony.GuildResource.AddMemberRoleWithReason, harmony.GuildResource.UnbanWithReason, etc.

    What did you expect to see? harmony.GuildResource.RemoveMemberRoleWithReason

    What version of the module are you using ? v0.18.0

    :)

  • Make return types for harmony.apiError consistent

    Make return types for harmony.apiError consistent

    This PR makes harmony.apiError return pointers to errors in all cases. Before, a pointer would not be returned for an APIError, but a pointer would be returned for a ValidationError which led to some slightly confusing and unexpected behaviour that was also inconsistent with functions that called apiError, like harmony.ChannelResource.Message.

  • Change package layout

    Change package layout

    Description

    This PR is a pretty big refactoring to prepare the ground for the API & Gateway v8 PR.

    Most notable changes:

    • A new discord package has been created to hold every Discord objects as well as some utility functions to work with them
    • "Resources" objects have been moved under their own package: channel, guild, invite, user and webhook
    • A new discord.Time type has been introduced to handle zero-value timestamps properly (discord.TimeFromStd and discord.Time.Std functions/methods have been added to help dealing with this new type)
    • Default log level is now correct
    • A potential race condition when reconnecting to the Gateway has been fixed
    • Heartbeating should be more reliable
    • State now returns Group DMs and DMs channels correctly
    • Documentation has been updated to reflect those changes

    :warning: This PR is breaking stuff :warning:

    Code written before this PR will very likely need to be updated to be compatible. Worry not (too much) though, the major breaking change is that most structures that were previously defined in the root package harmony now live under a new discord package. Settings and parameters structures have also been moved to this new package. If you want to see an overview of what needs to be changed, take a look at harmony_test.go, it showcases most changes required, if not all.

  • Add support for Guild Invite events

    Add support for Guild Invite events

    Description

    This PR adds support for two new events: Guild Invite Create and Guild Invite Delete. Two handlers have been added to the Client to register callbacks to those events: OnGuildInviteCreate and OnGuildInviteDelete.

  • Add support for the Delete All Reactions for Emoji endpoint

    Add support for the Delete All Reactions for Emoji endpoint

    Description

    This PR adds support for the Delete All Reactions for Emoji endpoint as well as for the corresponding Gateway event.

    A new RemoveAllReactionsForEmoji method has been added to the Channel object and a new OnMessageReactionRemoveEmoji method has been added to the Client.

  • Add support for Gateway Intents

    Add support for Gateway Intents

    Description

    This PR adds support for Gateway Intents. A new function has been added to choose which intents a client should subscribe to when it's created:

    // WithGatewayIntents allows to customize which Gateway Intents the client should subscribe to.
    // See https://discord.com/developers/docs/topics/gateway#gateway-intents for more information.
    // By default, the client subscribes to all events.
    func WithGatewayIntents(i GatewayIntent) ClientOption { /* ... */}
    

    By default, a client subscribes to all unprivileged intents.

    Also, a typo has been fixed in the WithGuildSubscriptions function.

  • Interaction Support?

    Interaction Support?

    Hi there,

    I'm sure you're busy but I wanted to ask if there was any plan to support Discord's new(ish?) interactions, specifically for slash commands. If there's no current plan to add the functionality, I am more than happy to PR it, I just didn't want to step on any toes.

    Thanks

  • Panic when getting a guild member's permissions.

    Panic when getting a guild member's permissions.

    Description When attempting to get a guild member's permissions a panic occurs. From my testing, it only occurs when using the instance of GuildMember passed with a new message. If you manually call a member with Guild(...).Member(...) and use that, the panic does not occur.

    What did you do? Within my message handler function, I call the Guild and Channel with the appropriate client methods. Then, I call *message.Member.PermissionsIn(...) and the panic occurs.

    What did you expect to see? A guild member's permissions in a channel printed to stdout.

    What did you see instead? A panic resulting from the call to PermissionsIn().

    What version of the module are you using ? v0.15.0

    Below is the code I used to test:

    package main
    
    import (
    	"context"
    	"fmt"
    	"os"
    	"os/signal"
    
    	"github.com/skwair/harmony"
    )
    
    type bot struct {
    	client *harmony.Client
    }
    
    func main() {
    	b := &bot{}
    	token := os.Getenv("HARMONY_TEST_TOKEN")
    	client, err := harmony.NewClient(token)
    
    	if err != nil {
    		fmt.Println(err)
    		return
    	}
    
    	b.client = client
    	b.client.OnMessageCreate(b.message)
    
    	if err := b.client.Connect(context.Background()); err != nil {
    		fmt.Println(err)
    		return
    	}
    	defer b.client.Disconnect()
    
    	close := make(chan os.Signal, 1)
    	signal.Notify(close, os.Interrupt)
    	<-close
    }
    
    func (b *bot) message(m *harmony.Message) {
    	if m.Content == "!perms" {
    		guild, err := b.client.Guild(m.GuildID).Get(context.Background())
    
    		if err != nil {
    			fmt.Println(err)
    			return
    		}
    
    		channel, err := b.client.Channel(m.ChannelID).Get(context.Background())
    
    		if err != nil {
    			fmt.Println(err)
    			return
    		}
    
    		var permissions int
    
    		// With GuildMember from API
    		member, err := b.client.Guild(m.GuildID).Member(context.Background(), m.Author.ID)
    
    		if err != nil {
    			fmt.Println(err)
    			return
    		}
    
    		permissions = member.PermissionsIn(guild, channel)
    		fmt.Println("API:", permissions)
    
    		// With GuildMember from Message
    		permissions = m.Member.PermissionsIn(guild, channel)
    		fmt.Println("Message:", permissions)
    
    	}
    }
    

    And the output from that:

    λ go run .
    API:  2146958591
    panic: runtime error: invalid memory address or nil pointer dereference
    [signal 0xc0000005 code=0x0 addr=0x0 pc=0x66f60d]
    
    goroutine 39 [running]:
    github.com/skwair/harmony.computeBasePermissions(0xc000214680, 0xc000107f40, 0xc000167930)
            C:/Users/Eli/go/pkg/mod/github.com/skwair/[email protected]/guild_permission.go:7 +0x3d
    github.com/skwair/harmony.(*GuildMember).PermissionsIn(0xc000107f40, 0xc000214680, 0xc0001dab40, 0x2)
            C:/Users/Eli/go/pkg/mod/github.com/skwair/[email protected]/guild_member.go:27 +0x3c
    main.(*bot).message(0xc00009c018, 0xc000108a80)
            C:/Users/Eli/projects/go/harmony-test/main.go:71 +0x419
    github.com/skwair/harmony.messageCreateHandler.handle(0xc00005abf0, 0x69dae0, 0xc000108a80)
            C:/Users/Eli/go/pkg/mod/github.com/skwair/[email protected]/events.go:329 +0x48
    created by github.com/skwair/harmony.(*Client).handle
            C:/Users/Eli/go/pkg/mod/github.com/skwair/[email protected]/dispatch.go:351 +0xe0
    exit status 2
    
Tg-channel-api - A JSON API for latest messages of public Telegram channels

Telegram Channel API A JSON API for latest messages of public Telegram channels.

Oct 27, 2022
Telego is Telegram Bot API library for Golang with full API implementation (one-to-one)
Telego is Telegram Bot API library for Golang with full API implementation (one-to-one)

Telego • Go Telegram Bot API Telego is Telegram Bot API library for Golang with full API implementation (one-to-one) The goal of this library was to c

Jan 5, 2023
Golang telegram bot API wrapper, session-based router and middleware

go-tgbot Pure Golang telegram bot API wrapper generated from swagger definition, session-based routing and middlewares. Usage benefits No need to lear

Nov 16, 2022
Client lib for Telegram bot api

Micha Client lib for Telegram bot api. Supports Bot API v2.3.1 (of 4th Dec 2016). Simple echo bot example: package main import ( "log" "git

Nov 10, 2022
Go library for Telegram Bot API
Go library for Telegram Bot API

tbot - Telegram Bot Server Features Full Telegram Bot API 4.7 support Zero dependency Type-safe API client with functional options Capture messages by

Nov 30, 2022
Golang bindings for the Telegram Bot API

Golang bindings for the Telegram Bot API All methods are fairly self explanatory, and reading the godoc page should explain everything. If something i

Jan 6, 2023
Full-native go implementation of Telegram API
Full-native go implementation of Telegram API

MTProto Full-native implementation of MTProto protocol on Golang! english русский 简体中文 Features Full native implementation All code, from sending requ

Jan 1, 2023
Bot that polls activity API for Github organisation and pushes updates to Telegram.

git-telegram-bot Telegram bot for notifying org events Requirements (for building) Go version 1.16.x Setup If you don't have a telegram bot token yet,

Apr 8, 2022
Flexible message router add-on for go-telegram-bot-api library.
Flexible message router add-on for go-telegram-bot-api library.

telemux Flexible message router add-on for go-telegram-bot-api library. Table of contents Motivation Features Minimal example Documentation Changelog

Oct 24, 2022
WIP Telegram Bot API server in Go

botapi The telegram-bot-api, but in go. WIP. Reference: https://core.telegram.org/bots/api Reference implementation: https://github.com/tdlib/telegram

Jan 2, 2023
send current weather updates from openweathermap api to your slack profile status using github action
send current weather updates from openweathermap api to your slack profile status using github action

go-slack-weather Send current weather updates from OpenWeatherMap API to your Slack profile status using GitHub Action Contents Setup Change Update Pe

Oct 31, 2022
A bot based on Telegram Bot API written in Golang allows users to download public Instagram photos, videos, and albums without receiving the user's credentials.

InstagramRobot InstagramRobot is a bot based on Telegram Bot API written in Golang that allows users to download public Instagram photos, videos, and

Dec 16, 2021
Golang bindings for the Telegram Bot API

Golang bindings for the Telegram Bot API All methods are fairly self explanatory, and reading the godoc page should explain everything. If something i

Nov 18, 2021
Golang client for compound.finace api and smart contracts

go-compound WARNING: this code deals with money both by making blockchain calls and returning information that can be used to lose/gain money. Please

Apr 5, 2022
This is an api for the penal system.

Penal api for discord bots Prototype phase Features Fast & Easy to use Developable Packages log net/http math/rand encoding/json strconv Mux Installat

Dec 5, 2021
DiscordGo: a Go package that provides low level bindings to the Discord chat client API
DiscordGo: a Go package that provides low level bindings to the Discord chat client API

DiscordGo DiscordGo is a Go package that provides low level bindings to the Discord chat client API. DiscordGo has nearly complete support for all of

Dec 14, 2021
UcodeQrTelebot ver2 - Easy way to get QR and U-code using Utopia API in telegram bot
UcodeQrTelebot ver2 - Easy way to get QR and U-code using Utopia API in telegram bot

UcodeQrTelebot Easy way to get QR and U-code using Utopia API in telegram bot Us

Dec 30, 2021
Quote-bot - Un bot utilisant l'API Twitter pour tweeter une citation par jour sur la programmation et les mathématiques.

Description Ceci est un simple bot programmé en Golang qui tweet une citation sur la programmation tout les jours. Ce bot est host sur le compte Twitt

Jan 1, 2022
Twitchbot - Go Twitch Bot Api wrapper, with an easy to use interface.

twitchbot Go Twitch Bot Api wrapper, with an easy to use interface. Example package main import ( "twitch/twitchbot" ) func main() { bot := twitch

Jan 8, 2022