[Release] FiveM To Discord

@Nico_West
Are you willing to test a new version to confirm if it works? If so, dm me please.

Updated to v1.4.3



  • Fixed Chat not working for some Player [Thanks to @Jellyton ]


Download in the First Post!

Updated to v1.4.4



  • Fixed Steam Profile Picture Issue


Download in the First Post!

1 Like

Is there a way to update automatically?

No, not now. For now you have to manually download the update or type update DiscordBot when it offers this option in the RCON Log

@CakeArmy_Max

[Release] D2FiveM - Control your FXServer with Discord

Is there a way that you can set this to send certain chat messages to certain channels like you did with breaking the three channels for Chat / System / Deaths ? For instance, send any /911 command to it’s own channel?

1 Like

Should be possible, I will look into this later.

This bot is a real hard finger, and works perfectly.

Or a possibility for us to create such things.

If you are capable of scripting something yourself, yes.

Otherwise, probably not…

Yes if I just have a start. lol :smiley:

You can find some very helpful tutorials at #development:tutorials
Also these links may come in handy:

FiveM Wiki
FiveM Natives reference
FiveM Docs
GTAForums

Also check the resources of other ppl and how they did certain things, that helped me a lot when I started :slight_smile:

2 Likes

There is. Just copy my code and you should be fine. This will send all the special commands to a separate channel.

Note: This is my first try to understand and edit lua. I could have made some mistakes but it works.

Config.lua

DiscordWebhookSystemInfos = 'WEBHOOK_LINK_HERE'
DiscordWebhookKillinglogs = 'WEBHOOK_LINK_HERE'
DiscordWebhookChat = 'WEBHOOK_LINK_HERE'
DiscordWebhookDispatch = 'WEBHOOK_LINK_HERE'

SystemAvatar = 'https://wiki.fivem.net/w/images/d/db/FiveM-Wiki.png'

UserAvatar = 'https://i.imgur.com/KIcqSYs.png'

SystemName = 'SYSTEM'


--[[ Special Commands formatting
		 *YOUR_TEXT*			--> Make Text Italics in Discord
		**YOUR_TEXT**			--> Make Text Bold in Discord
	   ***YOUR_TEXT***			--> Make Text Italics & Bold in Discord
		__YOUR_TEXT__			--> Underline Text in Discord
	   __*YOUR_TEXT*__			--> Underline Text and make it Italics in Discord
	  __**YOUR_TEXT**__			--> Underline Text and make it Bold in Discord
	 __***YOUR_TEXT***__		--> Underline Text and make it Italics & Bold in Discord
		~~YOUR_TEXT~~			--> Strikethrough Text in Discord
]]
-- Use 'USERNAME_NEEDED_HERE' without the quotes if you need a Users Name in a special command
-- Use 'USERID_NEEDED_HERE' without the quotes if you need a Users ID in a special command


-- These Special Commands will be printed differently in Discord, depending on what you set it to
SpecialCommands = {
				   {'/911', '__**[911]**__:  Dispatch to civilian channel!'},
				  }

						
-- These Blacklisted Commands will not be printed in Discord
BlacklistedCommands = {
					   '/fix',
					   '/revive',
					  }


server.lua

-- JUST EDIT THE CONFIG.LUA! -- JUST EDIT THE CONFIG.LUA! -- JUST EDIT THE CONFIG.LUA! -- JUST EDIT THE CONFIG.LUA!
-- JUST EDIT THE CONFIG.LUA! -- JUST EDIT THE CONFIG.LUA! -- JUST EDIT THE CONFIG.LUA! -- JUST EDIT THE CONFIG.LUA!
-- JUST EDIT THE CONFIG.LUA! -- JUST EDIT THE CONFIG.LUA! -- JUST EDIT THE CONFIG.LUA! -- JUST EDIT THE CONFIG.LUA!

-- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE!
-- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE!
-- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE!
-- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE!
-- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE!
-- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE! -- DO NOT EDIT THESE!

-- Error Check
if DiscordWebhookSystemInfos == nil and DiscordWebhookKillinglogs == nil and DiscordWebhookChat == nil and DiscordWebhookDisp[atch == nil then
	local Content = LoadResourceFile(GetCurrentResourceName(), 'config.lua')
	Content = load(Content)
	Content()
end
if DiscordWebhookSystemInfos == 'WEBHOOK_LINK_HERE' then
	print('\n\nERROR\n' .. GetCurrentResourceName() .. ': Please add your "System Infos" Webhook\n\n')
else
	PerformHttpRequest(DiscordWebhookSystemInfos, function(Error, Content, Head)
		if Content == '{"code": 50027, "message": "Invalid Webhook Token"}' then
			print('\n\nERROR\n' .. GetCurrentResourceName() .. ': "System Infos" Webhook non-existing!\n\n')
		end
	end)
end
if DiscordWebhookKillinglogs == 'WEBHOOK_LINK_HERE' then
	print('\n\nERROR\n' .. GetCurrentResourceName() .. ': Please add your "Killing Log" Webhook\n\n')
else
	PerformHttpRequest(DiscordWebhookKillinglogs, function(Error, Content, Head)
		if Content == '{"code": 50027, "message": "Invalid Webhook Token"}' then
			print('\n\nERROR\n' .. GetCurrentResourceName() .. ': "Killing Log" Webhook non-existing!\n\n')
		end
	end)
end
if DiscordWebhookChat == 'WEBHOOK_LINK_HERE' then
	print('\n\nERROR\n' .. GetCurrentResourceName() .. ': Please add your "Chat" Webhook\n\n')
else
	PerformHttpRequest(DiscordWebhookChat, function(Error, Content, Head)
		if Content == '{"code": 50027, "message": "Invalid Webhook Token"}' then
			print('\n\nERROR\n' .. GetCurrentResourceName() .. ': "Chat" Webhook non-existing!\n\n')
		end
	end)
end
if DiscordWebhookDispatch == 'WEBHOOK_LINK_HERE' then
	print('\n\nERROR\n' .. GetCurrentResourceName() .. ': Please add your "Dispatch" Webhook\n\n')
else
	PerformHttpRequest(DiscordWebhookChat, function(Error, Content, Head)
		if Content == '{"code": 50027, "message": "Invalid Webhook Token"}' then
			print('\n\nERROR\n' .. GetCurrentResourceName() .. ': "Dispatch" Webhook non-existing!\n\n')
		end
	end)
end
	
-- System Infos
PerformHttpRequest(DiscordWebhookSystemInfos, function(Error, Content, Head) end, 'POST', json.encode({username = SystemName, content = '**FiveM Server Webhook Started**'}), { ['Content-Type'] = 'application/json' })

AddEventHandler('playerConnecting', function()
	ToDiscord(DiscordWebhookSystemInfos, SystemName, '```css\n' .. GetPlayerName(source) .. ' connecting\n```', SystemAvatar)
end)

AddEventHandler('playerDropped', function(Reason)
	ToDiscord(DiscordWebhookSystemInfos, SystemName, '```fix\n' .. GetPlayerName(source) .. ' left (' .. Reason .. ')\n```', SystemAvatar)
end)


-- Killing Log
RegisterServerEvent('PlayerDied')
AddEventHandler('PlayerDied', function(Message, Weapon)
	local date = os.date('*t')
	
	if date.day < 10 then date.day = '0' .. tostring(date.day) end
	if date.month < 10 then date.month = '0' .. tostring(date.month) end
	if date.hour < 10 then date.hour = '0' .. tostring(date.hour) end
	if date.min < 10 then date.min = '0' .. tostring(date.min) end
	if date.sec < 10 then date.sec = '0' .. tostring(date.sec) end
	if Weapon then
		Message = Message .. ' [' .. Weapon .. ']'
	end
	ToDiscord(DiscordWebhookKillinglogs, SystemName, Message .. ' `' .. date.day .. '.' .. date.month .. '.' .. date.year .. ' - ' .. date.hour .. ':' .. date.min .. ':' .. date.sec .. '`', SystemAvatar)
end)


-- Chat
AddEventHandler('chatMessage', function(Source, Name, Message)
	--Removing Color Codes (^0, ^1, ^2 etc.) from the name and the message
	for i = 0, 9 do
		Message = Message:gsub('%^' .. i, '')
		Name = Name:gsub('%^' .. i, '')
	end
	
	--Checking if the message contains a special command
	if IsCommand(Message, 'Special') then
		Message = ReplaceSpecialCommand(Message, Source)
		
		--Getting the steam avatar
		local AvatarURL = UserAvatar
		if GetIDFromSource('steam', Source) then
			print('Using Steam')
			local SteamIDHex = GetIDFromSource('steam', Source)
			local SteamIDInt = tonumber(SteamIDHex, 16)
			PerformHttpRequest('http://steamcommunity.com/profiles/' .. SteamIDInt .. '/?xml=1', function(Error, Content, Head)
				local SteamProfileSplitted = stringsplit(Content, '\n')
				for i, Line in ipairs(SteamProfileSplitted) do
					if Line:find('<avatarFull>') then
						local AvatarURL = Line:gsub('	<avatarFull><!%[CDATA%[', ''):gsub(']]></avatarFull>', '')
						ToDiscord(DiscordWebhookDispatch, Name .. ' [ID: ' .. Source .. ']', Message, AvatarURL) --Sending the message to discord
						break
					end
				end
			end)
		else
			ToDiscord(DiscordWebhookDispatch, Name .. ' [ID: ' .. Source .. ']', Message, AvatarURL) --Sending the message to discord
		end
		return false
	end
	
	-- Shortens the Name, if needed
	if Name:len() > 23 then
		Name = Name:sub(1, 23)
	end

	--Checking if the message contains a blacklisted command
	if not IsCommand(Message, 'Blacklisted') then
	
		--Getting the steam avatar
		local AvatarURL = UserAvatar
		if GetIDFromSource('steam', Source) then
			print('Using Steam')
			local SteamIDHex = GetIDFromSource('steam', Source)
			local SteamIDInt = tonumber(SteamIDHex, 16)
			PerformHttpRequest('http://steamcommunity.com/profiles/' .. SteamIDInt .. '/?xml=1', function(Error, Content, Head)
				local SteamProfileSplitted = stringsplit(Content, '\n')
				for i, Line in ipairs(SteamProfileSplitted) do
					if Line:find('<avatarFull>') then
						local AvatarURL = Line:gsub('	<avatarFull><!%[CDATA%[', ''):gsub(']]></avatarFull>', '')
						ToDiscord(DiscordWebhookChat, Name .. ' [ID: ' .. Source .. ']', Message, AvatarURL) --Sending the message to discord
						break
					end
				end
			end)
		else
			ToDiscord(DiscordWebhookChat, Name .. ' [ID: ' .. Source .. ']', Message, AvatarURL) --Sending the message to discord
		end
	end
end)

-- Functions

function ToDiscord(WebHook, Name, Message, Image)
	if Message == nil or Message == '' then
		return false
	end
	
	PerformHttpRequest(WebHook, function(Error, Content, Head) end, 'POST', json.encode({username = Name, content = Message, avatar_url = Image}), { ['Content-Type'] = 'application/json' })
end

function IsCommand(String, Type)
	local StringSplitted = stringsplit(String, ' ')
	if Type == 'Blacklisted' then
		for i, BlacklistedCommand in ipairs(BlacklistedCommands) do
			if StringSplitted[1]:lower() == BlacklistedCommand:lower() then
				return true
			end
		end
	elseif Type == 'Special' then
		for i, SpecialCommand in ipairs(SpecialCommands) do
			if StringSplitted[1]:lower() == SpecialCommand[1]:lower() then
				return true
			end
		end
	end
	return false
end

function ReplaceSpecialCommand(String, Source)
	local StringSplitted = stringsplit(String, ' ')
	for i, SpecialCommand in ipairs(SpecialCommands) do
		if StringSplitted[1]:lower() == SpecialCommand[1]:lower() then
			StringSplitted[1] = SpecialCommand[2]
			local newString = ''
			for k, StringPart in ipairs(StringSplitted) do
				if newString == '' then
					newString = StringPart
				else
					newString = newString .. ' ' .. StringPart
				end
			end
			newString = newString:gsub('USERNAME_NEEDED_HERE', GetPlayerName(Source))
			newString = newString:gsub('USERID_NEEDED_HERE', Source)
			return newString
		end
	end
end

function stringsplit(input, seperator)
	if seperator == nil then
		seperator = '%s'
	end
	
	local t={} ; i=1
	
	for str in string.gmatch(input, '([^'..seperator..']+)') do
		t[i] = str
		i = i + 1
	end
	
	return t
end

function GetIDFromSource(Type, ID) --(Thanks To WolfKnight [forum.FiveM.net])
    local IDs = GetPlayerIdentifiers(ID)
    for k, CurrentID in pairs(IDs) do
        local ID = stringsplit(CurrentID, ':')
        if (ID[1]:lower() == string.lower(Type)) then
            return ID[2]:lower()
        end
    end
    return nil
end

-- Version Checking down here, better don't touch this
local CurrentVersion = '1.4.4'
local UpdateAvailable = false
local GithubResourceName = 'DiscordBot'

PerformHttpRequest('https://raw.githubusercontent.com/Flatracer/' .. GithubResourceName .. '_Resources/master/VERSION', function(Error, NewestVersion, Header)
	PerformHttpRequest('https://raw.githubusercontent.com/Flatracer/' .. GithubResourceName .. '_Resources/master/CHANGES', function(Error, Changes, Header)
		PerformHttpRequest('https://raw.githubusercontent.com/Flatracer/' .. GithubResourceName .. '_Resources/master/PREVIOUSVERSION', function(Error, PreviousVersion, Header)
			print('\n')
			print('##############')
			print('## ' .. GithubResourceName)
			print('##')
			print('## Current Version: ' .. CurrentVersion)
			print('## Newest Version: ' .. NewestVersion)
			print('##')
			if CurrentVersion ~= NewestVersion then
				if CurrentVersion == PreviousVersion then
					UpdateAvailable = true
				end
				print('## Outdated')
				print('## Check the Topic')
				if UpdateAvailable then
					print('## Or type "update ' .. GetCurrentResourceName() .. '"')
				end
				print('## For the newest Version!')
				print('##############')
				print('CHANGES: ' .. Changes)
			else
				UpdateAvailable = false
				print('## Up to date!')
				print('##############')
			end
			print('\n')
		end)
	end)
end)

-- Instant Update down here, better don't touch this as well
AddEventHandler('rconCommand', function(CMDName, Arguments)
    if CMDName:lower() == 'update' then
		if #Arguments == 1 then
			if Arguments[1]:lower() == GetCurrentResourceName():lower() then
				TriggerEvent(GetCurrentResourceName() .. ':StartUpdate')
			end
		else
			print('Argument count mismatch (Passed: ' .. #Arguments .. ', Wanted: 1)')
		end
		CancelEvent()
	end
end)

RegisterServerEvent(GetCurrentResourceName() .. ':StartUpdate')
AddEventHandler(GetCurrentResourceName() .. ':StartUpdate', function()
	if UpdateAvailable then
		PerformHttpRequest('https://raw.githubusercontent.com/Flatracer/' .. GithubResourceName .. '_Resources/master/CHANGEDFILES', function(Error, Content, Header)
			ContentSplitted = stringsplit(Content, '\n')
			for k, Line in ipairs(ContentSplitted) do
				local PreviousContent = ''
				if Line:find('-add') then
					Line = Line:gsub('-add')
					PreviousContent = LoadResourceFile(GetCurrentResourceName(), Line) .. '\n'
				end
				PerformHttpRequest('https://raw.githubusercontent.com/Flatracer/' .. GithubResourceName .. '/master/' .. Line, function(Error, NewContent, Header)
					SaveResourceFile(GetCurrentResourceName(), Line, PreviousContent .. NewContent, -1)
				end)
			end
		end)
		print('Update finished! Enter "restart ' .. GetCurrentResourceName() .. '" now!')
	else
		print('This is already the newest version! [' .. CurrentVersion .. ']')
	end
end)


1 Like

Do you know a way to do this with a button?

For example:

We are using action menu to give us our weapons. Now I would like to make a submenu called “Radio”.
Is this submenu I would like to have some buttons. Maybe that I’m code 4 or something.

How would I do this? I’ve tried it for like 6 hours now but can’t get it to work. Can you please help me? :smiley:

Sweet. I’ll throw this on and test it. If everything works, you rock!

Dude, you rock.

It had a error on Line 13 of the server.lua. Just a [ where there shouldn’t have been. Fixed it and it works perfectly! Thank you.

FIX:

if DiscordWebhookSystemInfos == nil and DiscordWebhookKillinglogs == nil and DiscordWebhookChat == nil and DiscordWebhookDispatch == nil then

Updated to v1.5.0



  • Added the option to specify commands which have their own webhook link
  • Changed the ‘ToDiscord’ function to an event (Can be called from other resources now)


Download in the First Post!

1 Like

Is there a way to update the resource by typing a command via RCON?

This is not available for this update, with a reason. It would overwrite the config.lua and remove your configuration. So please update it yourself.