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:
Create a Discord application for your bot
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 codedetails
: 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 |
See also
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 codecode
: JSON error codetext
: 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
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.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 | ||
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
Python package
Modules of the Discord Proxy package that are relevant for clients.