Welcome to discordproxy’s documentation!

Operations Guide

Installation

Alliance Auth installation

This section describes how to install Discord Proxy into an existing Alliance Auth installation.

Install Discord Proxy

Note

This guide assumed a default installation according to the official Auth installation guide.

Login as root user, activate your venv and navigate to your Auth main folder:

cd /home/allianceserver/myauth

Install discordproxy from PyPI into the venv:

pip install discordproxy

Add Discord Proxy to your supervisor configuration for Auth.

Edit supervisor.conf in your current folder and add the below section. Make sure to replace YOUR-BOT-TOKEN with your current Discord bot token:

[program:discordproxy]
command=/home/allianceserver/venv/auth/bin/discordproxyserver --token "YOUR-BOT-TOKEN"
directory=/home/allianceserver/myauth/log
user=allianceserver
numprocs=1
autostart=true
autorestart=true
stopwaitsecs=120
stdout_logfile=/home/allianceserver/myauth/log/discordproxyserver.out
stderr_logfile=/home/allianceserver/myauth/log/discordproxyserver.err

Note

We do not recommend adding discordproxy to your myauth group, since it does not require to be restarted after myauth configuration changes like the other programs in that group.

Reload supervisor to activate the changes and start Discord Proxy:

supervisorctl reload

To verify Discord Proxy is up and running you can check it’s status:

supervisorctl status discordproxy

It should say “RUNNING”.

To verify your installation was successful we recommend to test your server.

Stand-alone installation

This section describes how to install Discord Proxy as standalone server.

Create a Discord bot account

Follow this guide to create your Discord bot:

  1. Create a Discord application for your bot

  2. Invite your bot to your Discord server

Install discordproxy on your server

Create a service user and switch to that user:

sudo adduser --disabled-login discordproxy
sudo su discordproxy

Setup a virtual environment for the server, activate it and update key packages:

cd /home/discordproxy
python3 -m venv venv
source venv/bin/activate

Update and install basic packages:

pip install -U pip
pip install wheel setuptools

Install discordproxy from PyPI into the venv:

pip install discordproxy
Add discordproxy to supervisor

Create a supervisor configuration file - /home/discordproxy/discordproxyserver.conf - with the below template:

[program:discordproxy]
command=/home/discordproxy/venv/bin/discordproxyserver --token "YOUR-BOT-TOKEN"
directory=/home/discordproxy
user=discordproxy
numprocs=1
autostart=true
autorestart=true
stopwaitsecs=120
stdout_logfile=/home/discordproxy/discordproxyserver.out
stderr_logfile=/home/discordproxy/discordproxyserver.err

Add discordproxy to your supervisor configuration and restart supervisor to activate the change:

ln -s /home/discordproxy/discordproxyserver.conf /etc/supervisor/conf.d
supervisorctl reload

To verify your installation was successful we recommend to test your server.

Test Discord Proxy server

To verify your Discord Proxy server is up and running send a direct message to yourself with the CLI tool (replace the number below with your own user ID):

discordproxymessage direct 12345678 "test"

Hint

Here is how you can find IDs on your Discord server: Where can I find my User/Server/Message ID?

Server configuration

Discord Proxy is designed to run via supervisor and can be configured with the below arguments. It comes with sensible defaults and will in most cases only need the Discord bot token to operate.

To configure your server just add/modify one of the below parameters in the respective command line of your supervisor configuration:

usage: discordproxyserver [-h] [--token TOKEN] [--host HOST] [--port PORT]
                          [--log-console-level {DEBUG,INFO,WARN,ERROR,CRITICAL}]
                          [--log-file-level {DEBUG,INFO,WARN,ERROR,CRITICAL}]
                          [--log-file-path LOG_FILE_PATH] [--version]

Server with HTTP API for sending messages to Discord

optional arguments:
  -h, --help            show this help message and exit
  --token TOKEN         Discord bot token. Can alternatively be specified as
                        environment variable DISCORD_BOT_TOKEN. (default:
                        None)
  --host HOST           server host address (default: 127.0.0.1)
  --port PORT           server port (default: 50051)
  --log-console-level {DEBUG,INFO,WARN,ERROR,CRITICAL}
                        Log level of log file (default: CRITICAL)
  --log-file-level {DEBUG,INFO,WARN,ERROR,CRITICAL}
                        Log level of log file (default: INFO)
  --log-file-path LOG_FILE_PATH
                        Path for storing the log file. If no path if provided,
                        the log file will be stored in the current working
                        folder (default: None)
  --version             show the program version and exit

Tools

Discord Proxy comes with a simple tool for sending messages to your Discord server from the command line. The main purpose of this tool is to check that the server is functioning correctly.

Here is how to send a direct message to a user:

discordproxymessage direct 12345678 "hi!"

The number is the user ID of the user to sent a message to. For more information run the command with the -h option.

Developer Guide

There are two different approaches on how to interact with Discord via Discord Proxy:

  • DiscordClient

  • gRPC

DiscordClient

DiscordClient is a class provided by Discord Proxy which aims to make it easy to interact with Discord in your code. Most of the complexity of the underlying gRPC protocol is hidden behind a simple API.

See also

For the documentation of the DiscordClient class see: Client

Sending a message

Here is a simple example for sending a direct message to a user:

from discordproxy.client import DiscordClient

client = DiscordClient()
client.create_direct_message(user_id=123456789, content="Hello, world!")

Hint

To test this script please replace the user ID with your own. Here is how you can find IDs on your Discord server: Where can I find my User/Server/Message ID?

Sending a message with an embed

Messages often include attachments called embeds. You can construct your own embeds with the provided Embed class.

from discordproxy.client import DiscordClient
from discordproxy.discord_api_pb2 import Embed

client = DiscordClient()
embed = Embed(description="Hello, world!")
client.create_direct_message(user_id=123456789, embed=embed)

See also

All common Discord objects are defined as Protobuf classes and can be found here: Protocol Documentation. These classes are used for creating objects for to methods of the DiscordClient and are returned by them.

Error handling

There are two classes of errors which can occur:

First, errors from the Discord API, e.g. that a user can not be found. These errors will generate a DiscordProxyHttpError exception.

Second, errors from the network connection with the Discord Proxy server, e.g. when the server is not up or a timeout occurs. These errors will generate a DiscordProxyGrpcError exception.

Both exceptions objects include additional details, e.g. the HTTP error code or the Discord specific JSON error code. Both exceptions are also inherited from DiscordProxyException, so for a very simple error handling you can just catch the top level exception.

Here is the same example from before, but now with some rudimentary error handling:

from discordproxy.client import DiscordClient
from discordproxy.exceptions import DiscordProxyException

client = DiscordClient()
try:
    client.create_direct_message(user_id=123456789, content="Hello, world!")
except DiscordProxyException as ex:
    print(f"An error occured when trying to create a message: {ex}")

Timeouts

All requests are synchronous and will usually complete almost instantly. However, sometimes it can take a few seconds longer for a request to complete due to Discord rate limiting, especially if you are running multiple request to Discord in parallel. There also might be issues with the network or the Discord API, which might case requests to go on for a long time (the hard timeout on the client side is about 30 minutes). In order to build a robust application we recommend to use sensible timeouts with all requests. Note that this timeout must cover the complete duration it takes for a request to compete and should therefore not be set too short.

You can define custom timeouts with when instanciating your client. Further, when a timeout occures the specical exception DiscordProxyTimeoutError will be raised. Here is an example:

from discordproxy.client import DiscordClient, DiscordProxyTimeoutError

client = DiscordClient(timeout=30)  # Defines a timeout of 30 seconds for all methods
try:
    client.create_direct_message(user_id=123456789, content="Hello, world!")
except DiscordProxyTimeoutError as ex:
    # handle timeout
    ...

gRPC

Alternatively you can use the gRPC protocol directly in your code to interact with Discord. This approach is more complex and requires a deeper understanding of gRPC. But it will also give you the most flexibility with full access to all gRPC features.

gRPC client example

Here is a hello code example for a gRPC client that is sending a direct “hello world” message to a user:

import grpc

from discordproxy.discord_api_pb2 import SendDirectMessageRequest
from discordproxy.discord_api_pb2_grpc import DiscordApiStub

# opens a channel to Discord Proxy
with grpc.insecure_channel("localhost:50051") as channel:
    # create a client for the DiscordApi service
    client = DiscordApiStub(channel)
    # create a request to use the SendDirectMessageRequest method of the service
    request = SendDirectMessageRequest(user_id=123456789, content="Hello, world!")
    # send the request to Discord Proxy
    client.SendDirectMessage(request)

gRPC error handling

If a gRPC request fails a grpc.RpcError exception will be raised. RPC errors return the context of the request, consisting of two fields:

  • code: the gRPC status code

  • details: a string with additional details about the error.

The most common errors you can except will be originating from calls to the Discord API. e.g. if a user is no longer a member of the guild the Discord API will return a http response code 404. Discord Proxy will map all HTTP response codes from Discord to a gRPC status codes and raise a gRPC error exceptions (see also gRPC status codes). In addition the details field of that exception will contain the full error information as JSON object (see also gRPC details).

Code Example

Here is an example on how to catch and process an error exception from your gRPC calls:

try:
    client.SendDirectMessage(request)
except grpc.RpcError as e:
    # print error code and details
    print(f"Code: {e.code()}")
    print(f"Details: {e.details()}")
gRPC status codes

Here is how HTTP error codes are mapped against gRPC status codes:

HTTP response code gRPC status code
400 (BAD REQUEST) INVALID_ARGUMENT
401 (UNAUTHORIZED) UNAUTHENTICATED
403 (FORBIDDEN) PERMISSION_DENIED
404 (NOT FOUND) NOT_FOUND
405 (METHOD NOT ALLOWED) INVALID_ARGUMENT
429 (TOO MANY REQUESTS) RESOURCE_EXHAUSTED
502 (GATEWAY UNAVAILABLE) UNAVAILABLE
504 (GATEWAY TIMEOUT) DEADLINE_EXCEEDED
5xx (SERVER ERROR) INTERNAL
gRPC details

The Discord API will return two types of error codes:

  • HTTP response code (e.g. 404 if a request user does not exist)

  • JSON error code (e.g. 30007 when the maximum number of webhooks is reached )

In addition the response may contain some additional error text. All that information will be encoded as JSON in the details attribute of the gRPC error exception. Here is an example:

{
    "type": "HTTPException",
    "status": 403,
    "code": 50001,
    "text": "Missing Access"
}

Legend:

  • status: HTTP status code

  • code: JSON error code

  • text: Error message

Note

For most cases it should be sufficient to deal with the status code. The JSON error code is only needed in some special cases.

To simplify dealing with the JSON error objects you can also use this helper from the djangoproxy package, which will parse the details and return them as handy named tuple:

from discordproxy.helpers import parse_error_details

try:
    client.SendDirectMessage(request)
except grpc.RpcError as e:
    details = parse_error_details(e)
    print(f"HTTP response code: {details.status}")
    print(f"JSON error code: {details.code}")
    print(f"Discord error message: {details.text}")

See also

For the documentation of all helpers see: Helpers

gRPC timeouts

Here is how to use timeout with requests to the Discord Proxy. All timeouts are in seconds:

try:
    client.SendDirectMessage(request, timeout=10)
except grpc.RpcError as e:
    # handle timeouts

Should a timeout be triggered the client will receive a grpc.RpcError with status code DEADLINE_EXCEEDED.

Rate limiting

The Discord API is imposing rate limiting to all requests. Discord Proxy will automatically adhere to those rate limits by suspending a request until it can be sent. This can in certain situations result in requests taking a longer time to complete. If you need to complete your Discord request within a certain time, please see the sections about how to set custom timeouts.

API

The API is defined with Google’s Protocol Buffers (aka protobuf). It consists of the service DiscordApi and it’s methods.

Please see Protocol Documentation for a complete documentation of the service with it’s methods and messages.

Or if you want to generate your own gPRC client you can find the protobuf definition files in the /protobufs folder.

In addition the Python pacakge contains an overview of the gRPC classes and helpers useful for gRPC clients.

Protocol Documentation

Table of Contents

Top

discord_api.proto

Discord API service

This file contains all messages and services currently supported by Discord Proxy

Channel

Source: https://discord.com/developers/docs/resources/channel#channel-object

Field Type Label Description
id uint64
type Channel.Type
guild_id uint64
position int32
name string tbd //
topic string
nsfw bool
last_message_id uint64
parent_id uint64

Embed

Source: https://discord.com/developers/docs/resources/channel#embed-object-embed-structure

Field Type Label Description
title string
type string
description string
url string
timestamp string
color int32
footer Embed.Footer
image Embed.Image
thumbnail Embed.Thumbnail
video Embed.Video
provider Embed.Provider
author Embed.Author
fields Embed.Field repeated

Embed.Author
Field Type Label Description
name string
url string
icon_url string
proxy_icon_url string

Embed.Field
Field Type Label Description
name string
value string
inline bool

Embed.Image
Field Type Label Description
url string
proxy_icon_url string
height int32
width int32

Embed.Provider
Field Type Label Description
name string
url string

Embed.Thumbnail
Field Type Label Description
url string
proxy_url string
height int32
width int32

Embed.Video
Field Type Label Description
url string
proxy_icon_url string
height int32
width int32

Emoji

Source: https://discord.com/developers/docs/resources/emoji#emoji-object

Field Type Label Description
id uint64
name string
roles uint64 repeated
user User
required_colons bool
managed bool
animated bool
available bool

GetGuildChannelsRequest
Field Type Label Description
guild_id uint64

GetGuildChannelsResponse
Field Type Label Description
channels Channel repeated

GuildMember

Source: https://discord.com/developers/docs/resources/guild#guild-member-object

Field Type Label Description
user User
nick string
roles uint64 repeated
joined_at string
premium_since string
deaf bool
mute bool
pending bool
permissions string

Message

Source: https://discord.com/developers/docs/resources/channel#message-object

Field Type Label Description
id uint64
channel_id uint64
guild_id uint64
author User
member GuildMember
content string
timestamp string
edited_timestamp string
tts bool
mention_everyone bool
mentions User repeated
mention_roles uint64 repeated
mention_channels uint64 repeated
attachments Message.Attachment repeated
embeds Embed repeated
reactions Message.Reaction repeated
nonce string
pinned bool
webhook_id uint64
type Message.Type
activity Message.Activity
application Message.Application
message_reference Message.Reference
flags int32
stickers Message.Sticker repeated
referenced_message Message interaction: not implemented

Message.Activity
Field Type Label Description
type Message.Activity.Type
party_id string

Message.Application
Field Type Label Description
id uint64
cover_image string
description string
icon string
name string

Message.Attachment
Field Type Label Description
id uint64
filename string
size int32
url string
proxy_url string
height int32
width int32

Message.ChannelMention
Field Type Label Description
id uint64
guild_id uint64
type Channel.Type
name string

Message.Reaction
Field Type Label Description
count int32
me bool
emoji Emoji

Message.Reference
Field Type Label Description
message_id uint64
channel_id uint64
guild_id uint64
fail_if_not_exists bool

Message.Sticker
Field Type Label Description
id uint64
pack_id uint64
name string
description string
tags string
asset string
preview_asset string
format_type Message.Sticker.Type

Role

Source: https://discord.com/developers/docs/topics/permissions#role-object

Field Type Label Description
id uint64
name string
color int32
hoist bool
position int32
permissions string
managed bool
mentionable bool
tags Role.Tag repeated

Role.Tag
Field Type Label Description
id uint64
integration_id uint64
premium_subscriber bool

SendChannelMessageRequest
Field Type Label Description
channel_id uint64
content string
embed Embed

SendChannelMessageResponse
Field Type Label Description
message Message

SendDirectMessageRequest
Field Type Label Description
user_id uint64
content string
embed Embed

SendDirectMessageResponse
Field Type Label Description
message Message

User

Source: https://discord.com/developers/docs/resources/user#user-object

Field Type Label Description
id uint64
username string
discriminator string
avatar string
bot bool
system bool
mfa_enabled bool
locale string
verified bool
email string
flags int32
premium_type int32
public_flags int32

Channel.Type
Name Number Description
UNDEFINED 0
GUILD_TEXT 1
DM 2
GUILD_VOICE 3
GROUP_DM 4
GUILD_CATEGORY 5
GUILD_NEWS 6 GUILD_STORE = 7; NO LONGER SUPORTED IN API

Message.Activity.Type
Name Number Description
UNDEFINED 0
JOIN 1
SPECTATE 2
LISTEN 3
JOIN_REQUESTS 4

Message.Sticker.Type
Name Number Description
UNDEFINED 0
PNG 1
APNG 2
LOTTIE 3

Message.Type
Name Number Description
DEFAULT 0
RECIPIENT_ADD 1
RECIPIENT_REMOVE 2
CALL 3
CHANNEL_NAME_CHANGE 4
CHANNEL_ICON_CHANGE 5
CHANNEL_PINNED_MESSAGE 6
GUILD_MEMBER_JOIN 7
USER_PREMIUM_GUILD_SUBSCRIPTION 8
USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1 9
USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2 10
USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3 11
CHANNEL_FOLLOW_ADD 12
GUILD_DISCOVERY_DISQUALIFIED 14
GUILD_DISCOVERY_REQUALIFIED 15
REPLY 19
APPLICATION_COMMAND 20

DiscordApi

Provides access to the Discord API

Method Name Request Type Response Type Description
SendChannelMessage SendChannelMessageRequest SendChannelMessageResponse Send a message to a guild channel
SendDirectMessage SendDirectMessageRequest SendDirectMessageResponse Send a direct message to a user
GetGuildChannels GetGuildChannelsRequest GetGuildChannelsResponse Get the list of channel for a guild

Scalar Value Types

.proto Type Notes C++ Java Python Go C# PHP Ruby
double double double float float64 double float Float
float float float float float32 float float Float
int32 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. int32 int int int32 int integer Bignum or Fixnum (as required)
int64 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. int64 long int/long int64 long integer/string Bignum
uint32 Uses variable-length encoding. uint32 int int/long uint32 uint integer Bignum or Fixnum (as required)
uint64 Uses variable-length encoding. uint64 long int/long uint64 ulong integer/string Bignum or Fixnum (as required)
sint32 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. int32 int int int32 int integer Bignum or Fixnum (as required)
sint64 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. int64 long int/long int64 long integer/string Bignum
fixed32 Always four bytes. More efficient than uint32 if values are often greater than 2^28. uint32 int int uint32 uint integer Bignum or Fixnum (as required)
fixed64 Always eight bytes. More efficient than uint64 if values are often greater than 2^56. uint64 long int/long uint64 ulong integer/string Bignum
sfixed32 Always four bytes. int32 int int int32 int integer Bignum or Fixnum (as required)
sfixed64 Always eight bytes. int64 long int/long int64 long integer/string Bignum
bool bool boolean boolean bool bool boolean TrueClass/FalseClass
string A string must always contain UTF-8 encoded or 7-bit ASCII text. string String str/unicode string string string String (UTF-8)
bytes May contain any arbitrary sequence of bytes. string ByteString str []byte ByteString string String (ASCII-8BIT)

Python package

Modules of the Discord Proxy package that are relevant for clients.

Client

Exceptions

gRPC classes

Helpers

Indices and tables