Roblox Discord Webhook Proxy Server

Thanks for using it! Let me know if you have any issues with the service or there’s any functionality I’m missing.

Pushed an update which will ban webhooks for abusing rate limits.
(You have to send requests very frequently for this to happen)

If you do get banned, just make a new webhook and send less requests. :+1:

5 Likes

I can’t thank you enough for this :smile:

1 Like

Thank you so much! I can finally re-add my mod call feature which we’ve been needing for a long time. :stuck_out_tongue:

2 Likes

Thanks bud, you saved my business! :slight_smile:

1 Like

New Roblox API provides appropriate information for scripts to abide by Discord’s rate limits.
If Discord lifts the Roblox user-agent ban, I will be shutting down this proxy.

In the event of the proxy shutting down, I’ll provide a library for you to use webhooks in your scripts.

2 Likes

I used this before Roblox got banned from Discord

While there are existing Discord libraries, none of them abide by Discord’s dynamic rate limits.

You could edit or provide a new script that abide by Discord’s dynamic rate limits, if Roblox get’s unban from Discord.

I made one pretty similar to yours at around the same time! It’s a bit of a bummer that it wasn’t advertised enough.

2 Likes

Running into a problem where embeds aren’t being posted when I send to the discord server. They all worked fine until around yesterday.

For example, if I run the following code in the command bar…

local HttpService = game:GetService("HttpService")

local suggestion_webhook = "https://discord.osyr.is/api/webhooks/" --removed the ID I used

local options = {}
options.timestamp = "2018-05-14T05:30:08Z"
options.description = "Test"
options.footer = {
    icon_url = "",
    text = "Test"
}
options.author = {
    name = "WrathOfTheElvhen",
    url = "https://www.roblox.com/users/29783818/profile",
    icon_url = game.Players:GetUserThumbnailAsync(
        29783818,
        Enum.ThumbnailType.HeadShot,
        Enum.ThumbnailSize.Size420x420
    )
}

local HookData = {
    ["embed"] = options,
    ["content"] = "Test"
}

HookData = HttpService:JSONEncode(HookData)

HttpService:PostAsync(suggestion_webhook, HookData)

It would only post the following:

image

Running the JSON in the Embed Visualizer works fine, so I have no idea what the problem is…

JSON Encoding
{
  "embed": 
    {
      "footer": {
        "icon_url": "",
        "text": "Test"
      },
      "description": "Test",
      "timestamp": "2018-05-14T05:30:08Z",
      "author": {
        "icon_url": "https://www.roblox.com/headshot-thumbnail/image?userId=29783818&width=420&height=420&format=png",
        "name": "WrathOfTheElvhen",
        "url": "https://www.roblox.com/users/29783818/profile"
      }
    }
  ,
  "content": "Test"
}

I’m pretty sure the rate limits are all good and that you didn’t ban anything. I even tested it in other servers and nothing…Do you perhaps have any idea what happened @Osyris?

1 Like

Webhooks are

{ "embeds":[
{ embed content }, { second embed content }
], "content":"Test"}

In your case, just do

local HookData = {
    ["embeds"] = {options},
    ["content"] = "Test"
}
5 Likes

The proxy server just forwards your POST payload to Discord, the only thing I change is your headers (which shouldn’t matter).

Seems like something on Discord’s end changed. Does @Semaphorism’s advice fix your problem?

1 Like

Ah, yeah, I see the problem. I was using [“embeds”] = {options} before, but I changed it because, while trying to find the problem, an embed visualizer stated that it should be “embed” not embeds. And when I did that, it would send (just not the embed). Changing it like @Semaphorism suggested still wouldn’t work in game, however a POST request using the JSON string I posted above would post just fine. Which I really should have tested first.

What I didn’t realize is that problem came about from the function I used to generate the timestamp, and that’s why it never posted. I must have done a bad job debugging it, because I completely missed that was the original problem.

Timestamp function
local function getCurrentTime()
	local currentTime = os.date("!*t")

	local hour = currentTime.hour
	local minute = currentTime.min
	local second = currentTime.sec

	local day = currentTime.day
	local month = currentTime.month
	local year = currentTime.year

	-- ISO8601 support
	if hour <= 10 then
		hour = 0 .. hour
	end
	if minute <= 10 then
		minute = 0 .. minute
	end
	if second <= 10 then
		second = 0 .. second
	end
	if day <= 10 then
		day = 0 .. day
	end
	if month <= 10 then
		month = 0 .. month
	end
	if year <= 10 then
		year = 0 .. year
	end

	return ("%s-%s-%sT%s:%s:%sZ"):format(year, month, day, hour, minute, second)
end

If you look you can probably see the problem. If the day was <= 10, then it’d add a leading zero. Well, today is the 10th, so it gave an incorrect timestamp format (“2018-08-010T00:00:00Z”) lol.

Sorry for the trouble! And thanks for the help and quick responses @Osyris and @Semaphorism

3 Likes

string.format supports padding like this example:

print(string.format("%012i", 300)) --> 000000000300

Flags and Width

Flag Description
- Left-justify the given field width. Right justification is the default (see “width” below).
+ Forces to precede by a + or - sign, even if the number is positive. The default only shows the - sign in front of a negative number.
(space) A blank space is inserted before the value.
# Used with o and x/X, writes a 0 (octal) or 0x/0X (hex) before the values for values other than zero. Used with e/E and f, it forces the written output to contain a decimal point even if no digits would follow. By default, if no digits follow, no decimal point is written. Used with g or G the result is the same as with e or E but trailing zeros are not removed.
0 Left-pads the number with 0s instead of empty spaces (see “width” below).
width Description
(number) Minimum number of characters to return. If the number of characters to be formatted is less than this number, then the result is padded with blank spaces. Otherwise, nothing happens.
1 Like

What is the limit? I got blocked. Oof.

Jeez what are you sending? I find it really hard to reach the limit with good use (never gotten close to it). You should make your system for it more efficient.

I didn’t receive an error about being block???, but my web-hook stopped sending information regarding server information. I ensured that whatever I am receiving on Discord is in a Embed utilizing the fields and wrapped it into a pcall. It doesn’t throw an error but my discord channel isn’t receiving the information.

1 Like

Can you show us your code minus the URL?

I was using a teleporter that would teleport players to my game from someone else’s slightly more popular game, and the teleporter would read back the following:
“(PlayerName) has been teleported to NASCAR '18 from (GameID) at (Time) on (Date)”

Did like 4000 teleports in 2 days

Edit:
My new method is the following scripts:

 -- Teleport Script
function onTouched(hit)
    local player = game.Players:GetPlayerFromCharacter(hit.Parent)
	script.Disabled = true
    if player then
			script.Parent.NumPlayers.Value = script.Parent.NumPlayers.Value + 1
        game:GetService("TeleportService"):Teleport(1917083441,player)
    end

	wait(1)
	script.Disabled = false
end
 
script.Parent.Touched:connect(onTouched)
-- DataPost Script
local http = game:GetService("HttpService")
local playernum = script.Parent.Teleporter.NumPlayers

while true do
wait(300)	
if playernum.Value > 0 then
local date = os.date("!*t")
local Data = {['content'] = tostring(playernum.Value).."players has been teleported to NASCAR '18 from ".. game.PlaceId .. " at " ..date.hour..":"..date.min .. " on " .. date.month.."/"..date.day.."/"..date.year}
		Data = http:JSONEncode(Data)
			http:PostAsync(url, Data)
playernum.Value = 0
end
end

Think that should keep me from hitting the limit.