Initial commit

This commit is contained in:
2025-04-15 22:27:20 -04:00
parent 5b7b68f81f
commit 771d8fe8e8
597 changed files with 149544 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
*.smx
discord_api.zip

View File

@@ -0,0 +1,39 @@
# sourcemod-discord
Discord API for Sourcemod. There's a lot more to add. Changes will be happening that will break existing plugins, sorry. Still a bit messy
# IMPORTANT
Before you are able to send messages, you have to create a websocket connection with the bot atleast once. Simply, do this:
http://scooterx3.net/2016-05-25/discord-bot.html
(Or paste this into browser -> console: http://pastebin.com/3g2HbTjY )
## Installation
- Put discord.inc in your include folder
- Compile discord_api.sp and put it in your sourcemod/plugins folder
- Install smjansson and SteamWorks extension (Might need to restart server after)
Optional examples:
- Edit the .sp by replacing `<Bot Token>` with your bot token.
- Compile and place in sourcemod/plugins
Bot Token can be obtained from:
https://discordapp.com/developers/applications/me
Create a new user and turn into bot account, reveal Token and copy.
## Features
- List all guilds
- List all Channels for guilds
- Send Messages to Channel
- Listen for messages for Channel
## API
For full list view `discord.inc` and take a look at `discord_test.sp` for examples.
### Note
> DiscordBot and DiscordChannel are both Handles. Everytime DiscordBot is passed, it's the same Handle to the Bot. This should not be closed unless you don't need that Bot anymore. DiscordChannel are handles to the Channel. Everytime Channel is passed the handle is closed after. If you need to keep the Channel Handle, Clone it. E.g `DiscordChannel newChannel = CloneHandle(Channel);`
### OnChannelMessage
Both Bot and channel are destroyed after it's called.

View File

@@ -0,0 +1,94 @@
/*
stock Handle PrepareRequest(DiscordBot bot, char[] url, EHTTPMethod method=k_EHTTPMethodGET, Handle hJson=null, SteamWorksHTTPDataReceived DataReceived = INVALID_FUNCTION, SteamWorksHTTPRequestCompleted RequestCompleted = INVALID_FUNCTION) {
static char stringJson[16384];
stringJson[0] = '\0';
if(hJson != null) {
json_dump(hJson, stringJson, sizeof(stringJson), 0, true);
}
//Format url
static char turl[128];
FormatEx(turl, sizeof(turl), "https://discordapp.com/api/%s", url);
Handle request = SteamWorks_CreateHTTPRequest(method, turl);
if(request == null) {
return null;
}
if(bot != null) {
BuildAuthHeader(request, bot);
}
SteamWorks_SetHTTPRequestRawPostBody(request, "application/json; charset=UTF-8", stringJson, strlen(stringJson));
SteamWorks_SetHTTPRequestNetworkActivityTimeout(request, 30);
if(RequestCompleted == INVALID_FUNCTION) {
//I had some bugs previously where it wouldn't send request and return code 0 if I didn't set request completed.
//This is just a safety then, my issue could have been something else and I will test more later on
RequestCompleted = HTTPCompleted;
}
if(DataReceived == INVALID_FUNCTION) {
//Need to close the request handle
DataReceived = HTTPDataReceive;
}
SteamWorks_SetHTTPCallbacks(request, RequestCompleted, HeadersReceived, DataReceived);
if(hJson != null) delete hJson;
return request;
}
*/
methodmap DiscordRequest < Handle {
public DiscordRequest(char[] url, EHTTPMethod method) {
Handle request = SteamWorks_CreateHTTPRequest(method, url);
return view_as<DiscordRequest>(request);
}
public void SetJsonBody(Handle hJson) {
static char stringJson[16384];
stringJson[0] = '\0';
if(hJson != null) {
json_dump(hJson, stringJson, sizeof(stringJson), 0, true);
}
SteamWorks_SetHTTPRequestRawPostBody(this, "application/json; charset=UTF-8", stringJson, strlen(stringJson));
if(hJson != null) delete hJson;
}
public void SetJsonBodyEx(Handle hJson) {
static char stringJson[16384];
stringJson[0] = '\0';
if(hJson != null) {
json_dump(hJson, stringJson, sizeof(stringJson), 0, true);
}
SteamWorks_SetHTTPRequestRawPostBody(this, "application/json; charset=UTF-8", stringJson, strlen(stringJson));
}
property int Timeout {
public set(int timeout) {
SteamWorks_SetHTTPRequestNetworkActivityTimeout(this, timeout);
}
}
public void SetCallbacks(SteamWorksHTTPRequestCompleted OnComplete, SteamWorksHTTPDataReceived DataReceived) {
SteamWorks_SetHTTPCallbacks(this, OnComplete, HeadersReceived, DataReceived);
}
public void SetContextValue(any data1, any data2) {
SteamWorks_SetHTTPRequestContextValue(this, data1, data2);
}
public void SetData(any data1, char[] route) {
SteamWorks_SetHTTPRequestContextValue(this, data1, UrlToDP(route));
}
public void SetBot(DiscordBot bot) {
BuildAuthHeader(this, bot);
}
public void Send(char[] route) {
DiscordSendRequest(this, route);
}
}

View File

@@ -0,0 +1,152 @@
public int Native_DiscordBot_GetGuildChannels(Handle plugin, int numParams) {
DiscordBot bot = GetNativeCell(1);
char guild[32];
GetNativeString(2, guild, sizeof(guild));
Function fCallback = GetNativeCell(3);
Function fCallbackAll = GetNativeCell(4);
any data = GetNativeCell(5);
DataPack dp = CreateDataPack();
WritePackCell(dp, bot);
WritePackString(dp, guild);
WritePackCell(dp, plugin);
WritePackFunction(dp, fCallback);
WritePackFunction(dp, fCallbackAll);
WritePackCell(dp, data);
ThisSendRequest(bot, guild, dp);
}
static void ThisSendRequest(DiscordBot bot, char[] guild, DataPack dp) {
char url[64];
FormatEx(url, sizeof(url), "guilds/%s/channels", guild);
Handle request = PrepareRequest(bot, url, k_EHTTPMethodGET, null, GetGuildChannelsData);
if(request == null) {
CreateTimer(2.0, GetGuildChannelsDelayed, dp);
return;
}
SteamWorks_SetHTTPRequestContextValue(request, dp, UrlToDP(url));
DiscordSendRequest(request, url);
}
public Action GetGuildChannelsDelayed(Handle timer, any data) {
DataPack dp = view_as<DataPack>(data);
ResetPack(dp);
DiscordBot bot = ReadPackCell(dp);
char guild[32];
ReadPackString(dp, guild, sizeof(guild));
ThisSendRequest(bot, guild, dp);
}
public int GetGuildChannelsData(Handle request, bool failure, int offset, int statuscode, any dp) {
if(failure || statuscode != 200) {
if(statuscode == 429 || statuscode == 500) {
ResetPack(dp);
DiscordBot bot = ReadPackCell(dp);
char guild[32];
ReadPackString(dp, guild, sizeof(guild));
ThisSendRequest(bot, guild, view_as<DataPack>(dp));
delete request;
return;
}
LogError("[DISCORD] Couldn't Retrieve Guild Channels - Fail %i %i", failure, statuscode);
delete request;
delete view_as<Handle>(dp);
return;
}
SteamWorks_GetHTTPResponseBodyCallback(request, GetGuildChannelsData_Data, dp);
delete request;
}
public int GetGuildChannelsData_Data(const char[] data, any datapack) {
Handle hJson = json_load(data);
//Read from datapack to get info
Handle dp = view_as<Handle>(datapack);
ResetPack(dp);
int bot = ReadPackCell(dp);
char guild[32];
ReadPackString(dp, guild, sizeof(guild));
Handle plugin = view_as<Handle>(ReadPackCell(dp));
Function func = ReadPackFunction(dp);
Function funcAll = ReadPackFunction(dp);
any pluginData = ReadPackCell(dp);
delete dp;
//Create forwards
Handle fForward = INVALID_HANDLE;
Handle fForwardAll = INVALID_HANDLE;
if(func != INVALID_FUNCTION) {
fForward = CreateForward(ET_Ignore, Param_Cell, Param_String, Param_Cell, Param_Cell);
AddToForward(fForward, plugin, func);
}
if(funcAll != INVALID_FUNCTION) {
fForwardAll = CreateForward(ET_Ignore, Param_Cell, Param_String, Param_Cell, Param_Cell);
AddToForward(fForwardAll, plugin, funcAll);
}
ArrayList alChannels = null;
if(funcAll != INVALID_FUNCTION) {
alChannels = CreateArray();
}
//Loop through json
for(int i = 0; i < json_array_size(hJson); i++) {
Handle hObject = json_array_get(hJson, i);
DiscordChannel Channel = view_as<DiscordChannel>(hObject);
if(fForward != INVALID_HANDLE) {
Call_StartForward(fForward);
Call_PushCell(bot);
Call_PushString(guild);
Call_PushCell(Channel);
Call_PushCell(pluginData);
Call_Finish();
}
if(fForwardAll != INVALID_HANDLE) {
alChannels.Push(Channel);
}else {
delete Channel;
}
}
if(fForwardAll != INVALID_HANDLE) {
Call_StartForward(fForwardAll);
Call_PushCell(bot);
Call_PushString(guild);
Call_PushCell(alChannels);
Call_PushCell(pluginData);
Call_Finish();
for(int i = 0; i < alChannels.Length; i++) {
Handle hChannel = view_as<Handle>(alChannels.Get(i));
delete hChannel;
}
delete alChannels;
delete fForwardAll;
}
if(fForward != INVALID_HANDLE) {
delete fForward;
}
delete hJson;
}

View File

@@ -0,0 +1,165 @@
public int Native_DiscordBot_GetGuilds(Handle plugin, int numParams) {
DiscordBot bot = GetNativeCell(1);
Function fCallback = GetNativeCell(2);
Function fCallbackAll = GetNativeCell(3);
any data = GetNativeCell(4);
DataPack dp = CreateDataPack();
WritePackCell(dp, bot);
WritePackCell(dp, plugin);
WritePackFunction(dp, fCallback);
WritePackFunction(dp, fCallbackAll);
WritePackCell(dp, data);
ThisSendRequest(bot, dp);
}
static void ThisSendRequest(DiscordBot bot, DataPack dp) {
char url[64];
FormatEx(url, sizeof(url), "users/@me/guilds");
Handle request = PrepareRequest(bot, url, k_EHTTPMethodGET, null, GetGuildsData);
if(request == null) {
CreateTimer(2.0, GetGuildsDelayed, dp);
return;
}
SteamWorks_SetHTTPRequestContextValue(request, dp, UrlToDP(url));
DiscordSendRequest(request, url);
}
public Action GetGuildsDelayed(Handle timer, any data) {
DataPack dp = view_as<DataPack>(data);
ResetPack(dp);
DiscordBot bot = ReadPackCell(dp);
ThisSendRequest(bot, dp);
}
public int GetGuildsData(Handle request, bool failure, int offset, int statuscode, any dp) {
if(failure || statuscode != 200) {
if(statuscode == 429 || statuscode == 500) {
ResetPack(dp);
DiscordBot bot = ReadPackCell(dp);
ThisSendRequest(bot, dp);
delete request;
return;
}
LogError("[DISCORD] Couldn't Retrieve Guilds - Fail %i %i", failure, statuscode);
delete request;
delete view_as<Handle>(dp);
return;
}
SteamWorks_GetHTTPResponseBodyCallback(request, GetGuildsData_Data, dp);
delete request;
}
public int GetGuildsData_Data(const char[] data, any datapack) {
Handle hJson = json_load(data);
//Read from datapack to get info
Handle dp = view_as<Handle>(datapack);
ResetPack(dp);
int bot = ReadPackCell(dp);
Handle plugin = view_as<Handle>(ReadPackCell(dp));
Function func = ReadPackFunction(dp);
Function funcAll = ReadPackFunction(dp);
any pluginData = ReadPackCell(dp);
delete dp;
//Create forwards
Handle fForward = INVALID_HANDLE;
Handle fForwardAll = INVALID_HANDLE;
if(func != INVALID_FUNCTION) {
fForward = CreateForward(ET_Ignore, Param_Cell, Param_String, Param_String, Param_String, Param_Cell, Param_Cell, Param_Cell);
AddToForward(fForward, plugin, func);
}
if(funcAll != INVALID_FUNCTION) {
fForwardAll = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
AddToForward(fForwardAll, plugin, funcAll);
}
ArrayList alId = null;
ArrayList alName = null;
ArrayList alIcon = null;
ArrayList alOwner = null;
ArrayList alPermissions = null;
if(funcAll != INVALID_FUNCTION) {
alId = CreateArray(32);
alName = CreateArray(64);
alIcon = CreateArray(128);
alOwner = CreateArray();
alPermissions = CreateArray();
}
//Loop through json
for(int i = 0; i < json_array_size(hJson); i++) {
Handle hObject = json_array_get(hJson, i);
static char id[32];
static char name[64];
static char icon[128];
bool owner = false;
int permissions;
JsonObjectGetString(hObject, "id", id, sizeof(id));
JsonObjectGetString(hObject, "name", name, sizeof(name));
JsonObjectGetString(hObject, "icon", icon, sizeof(icon));
owner = JsonObjectGetBool(hObject, "owner");
permissions = JsonObjectGetBool(hObject, "permissions");
if(fForward != INVALID_HANDLE) {
Call_StartForward(fForward);
Call_PushCell(bot);
Call_PushString(id);
Call_PushString(name);
Call_PushString(icon);
Call_PushCell(owner);
Call_PushCell(permissions);
Call_PushCell(pluginData);
Call_Finish();
}
if(fForwardAll != INVALID_HANDLE) {
alId.PushString(id);
alName.PushString(name);
alIcon.PushString(icon);
alOwner.Push(owner);
alPermissions.Push(permissions);
}
delete hObject;
}
if(fForwardAll != INVALID_HANDLE) {
Call_StartForward(fForwardAll);
Call_PushCell(bot);
Call_PushCell(alId);
Call_PushCell(alName);
Call_PushCell(alIcon);
Call_PushCell(alOwner);
Call_PushCell(alPermissions);
Call_PushCell(pluginData);
Call_Finish();
delete alId;
delete alName;
delete alIcon;
delete alOwner;
delete alPermissions;
delete fForwardAll;
}
if(fForward != INVALID_HANDLE) {
delete fForward;
}
delete hJson;
}

View File

@@ -0,0 +1,156 @@
/**
* public native void GetGuildMembers(char[] guild, OnGetMembers fCallback, char[] afterUserID="", int limit=250);
*/
public int Native_DiscordBot_GetGuildMembers(Handle plugin, int numParams) {
DiscordBot bot = view_as<DiscordBot>(CloneHandle(GetNativeCell(1)));
char guild[32];
GetNativeString(2, guild, sizeof(guild));
Function fCallback = GetNativeCell(3);
int limit = GetNativeCell(4);
char afterID[32];
GetNativeString(5, afterID, sizeof(afterID));
Handle hData = json_object();
json_object_set_new(hData, "bot", bot);
json_object_set_new(hData, "guild", json_string(guild));
json_object_set_new(hData, "limit", json_integer(limit));
json_object_set_new(hData, "afterID", json_string(afterID));
Handle fwd = CreateForward(ET_Ignore, Param_Cell, Param_String, Param_Cell);
AddToForward(fwd, plugin, fCallback);
json_object_set_new(hData, "callback", json_integer(view_as<int>(fwd)));
GetMembers(hData);
}
public int Native_DiscordBot_GetGuildMembersAll(Handle plugin, int numParams) {
DiscordBot bot = view_as<DiscordBot>(CloneHandle(GetNativeCell(1)));
char guild[32];
GetNativeString(2, guild, sizeof(guild));
Function fCallback = GetNativeCell(3);
int limit = GetNativeCell(4);
char afterID[32];
GetNativeString(5, afterID, sizeof(afterID));
Handle hData = json_object();
json_object_set_new(hData, "bot", bot);
json_object_set_new(hData, "guild", json_string(guild));
json_object_set_new(hData, "limit", json_integer(limit));
json_object_set_new(hData, "afterID", json_string(afterID));
Handle fwd = CreateForward(ET_Ignore, Param_Cell, Param_String, Param_Cell);
AddToForward(fwd, plugin, fCallback);
json_object_set_new(hData, "callback", json_integer(view_as<int>(fwd)));
GetMembers(hData);
}
static void GetMembers(Handle hData) {
DiscordBot bot = view_as<DiscordBot>(json_object_get(hData, "bot"));
char guild[32];
JsonObjectGetString(hData, "guild", guild, sizeof(guild));
int limit = JsonObjectGetInt(hData, "limit");
char afterID[32];
JsonObjectGetString(hData, "afterID", afterID, sizeof(afterID));
char url[256];
if(StrEqual(afterID, "")) {
FormatEx(url, sizeof(url), "https://discordapp.com/api/guilds/%s/members?limit=%i", guild, limit);
}else {
FormatEx(url, sizeof(url), "https://discordapp.com/api/guilds/%s/members?limit=%i&afterID=%s", guild, limit, afterID);
}
char route[128];
FormatEx(route, sizeof(route), "guild/%s/members", guild);
DiscordRequest request = new DiscordRequest(url, k_EHTTPMethodGET);
if(request == null) {
delete bot;
CreateTimer(2.0, SendGetMembers, hData);
return;
}
request.SetCallbacks(HTTPCompleted, MembersDataReceive);
request.SetBot(bot);
request.SetData(hData, route);
request.Send(route);
delete bot;
}
public Action SendGetMembers(Handle timer, any data) {
GetMembers(view_as<Handle>(data));
}
public MembersDataReceive(Handle request, bool failure, int offset, int statuscode, any dp) {
if(failure || (statuscode != 200)) {
if(statuscode == 400) {
PrintToServer("BAD REQUEST");
}
if(statuscode == 429 || statuscode == 500) {
GetMembers(dp);
delete request;
return;
}
LogError("[DISCORD] Couldn't Send GetMembers - Fail %i %i", failure, statuscode);
delete request;
delete view_as<Handle>(dp);
return;
}
SteamWorks_GetHTTPResponseBodyCallback(request, GetMembersData, dp);
delete request;
}
public int GetMembersData(const char[] data, any dp) {
Handle hJson = json_load(data);
Handle hData = view_as<Handle>(dp);
DiscordBot bot = view_as<DiscordBot>(json_object_get(hData, "bot"));
Handle fwd = view_as<Handle>(JsonObjectGetInt(hData, "callback"));
char guild[32];
JsonObjectGetString(hData, "guild", guild, sizeof(guild));
if(fwd != null) {
Call_StartForward(fwd);
Call_PushCell(bot);
Call_PushString(guild);
Call_PushCell(hJson);
Call_Finish();
}
delete bot;
if(JsonObjectGetBool(hData, "autoPaginate")) {
int size = json_array_size(hJson);
int limit = JsonObjectGetInt(hData, "limit");
if(limit == size) {
Handle hLast = json_array_get(hJson, size - 1);
char lastID[32];
json_string_value(hLast, lastID, sizeof(lastID));
delete hJson;
delete hLast;
json_object_set_new(hData, "afterID", json_string(lastID));
GetMembers(hData);
return;
}
}
delete hJson;
delete hData;
delete fwd;
}

View File

@@ -0,0 +1,102 @@
public int Native_DiscordBot_GetGuildRoles(Handle plugin, int numParams) {
DiscordBot bot = view_as<DiscordBot>(CloneHandle(GetNativeCell(1)));
char guild[32];
GetNativeString(2, guild, sizeof(guild));
Function fCallback = GetNativeCell(3);
any data = GetNativeCell(4);
Handle hData = json_object();
json_object_set_new(hData, "bot", bot);
json_object_set_new(hData, "guild", json_string(guild));
json_object_set_new(hData, "data1", json_integer(view_as<int>(data)));
Handle fwd = CreateForward(ET_Ignore, Param_Cell, Param_String, Param_Cell, Param_Cell);
AddToForward(fwd, plugin, fCallback);
json_object_set_new(hData, "callback", json_integer(view_as<int>(fwd)));
GetGuildRoles(hData);
}
static void GetGuildRoles(Handle hData) {
DiscordBot bot = view_as<DiscordBot>(json_object_get(hData, "bot"));
char guild[32];
JsonObjectGetString(hData, "guild", guild, sizeof(guild));
char url[256];
FormatEx(url, sizeof(url), "https://discordapp.com/api/guilds/%s/roles", guild);
char route[128];
FormatEx(route, sizeof(route), "guild/%s/roles", guild);
DiscordRequest request = new DiscordRequest(url, k_EHTTPMethodGET);
if(request == null) {
delete bot;
CreateTimer(2.0, SendGetGuildRoles, hData);
return;
}
request.SetCallbacks(HTTPCompleted, GetGuildRolesReceive);
request.SetBot(bot);
request.SetData(hData, route);
request.Send(route);
delete bot;
}
public Action SendGetGuildRoles(Handle timer, any data) {
GetGuildRoles(view_as<Handle>(data));
}
public GetGuildRolesReceive(Handle request, bool failure, int offset, int statuscode, any dp) {
if(failure || (statuscode != 200)) {
if(statuscode == 400) {
PrintToServer("BAD REQUEST");
}
if(statuscode == 429 || statuscode == 500) {
GetGuildRoles(dp);
delete request;
return;
}
LogError("[DISCORD] Couldn't Send GetGuildRoles - Fail %i %i", failure, statuscode);
delete request;
delete view_as<Handle>(dp);
return;
}
SteamWorks_GetHTTPResponseBodyCallback(request, GetRolesData, dp);
delete request;
}
public int GetRolesData(const char[] data, any dp) {
Handle hJson = json_load(data);
Handle hData = view_as<Handle>(dp);
DiscordBot bot = view_as<DiscordBot>(json_object_get(hData, "bot"));
Handle fwd = view_as<Handle>(JsonObjectGetInt(hData, "callback"));
char guild[32];
JsonObjectGetString(hData, "guild", guild, sizeof(guild));
any data1 = JsonObjectGetInt(hData, "data1");
if(fwd != null) {
Call_StartForward(fwd);
Call_PushCell(bot);
Call_PushString(guild);
Call_PushCell(view_as<RoleList>(hJson));
Call_PushCell(data1);
Call_Finish();
}
delete bot;
delete hJson;
delete hData;
delete fwd;
}

View File

@@ -0,0 +1,145 @@
public int Native_DiscordBot_StartTimer(Handle plugin, int numParams) {
DiscordBot bot = GetNativeCell(1);
DiscordChannel channel = GetNativeCell(2);
Function func = GetNativeCell(3);
Handle hObj = json_object();
json_object_set(hObj, "bot", bot);
json_object_set(hObj, "channel", channel);
Handle fwd = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell);
AddToForward(fwd, plugin, func);
json_object_set_new(hObj, "callback", json_integer(view_as<int>(fwd)));
GetMessages(hObj);
}
public void GetMessages(Handle hObject) {
DiscordBot bot = view_as<DiscordBot>(json_object_get(hObject, "bot"));
DiscordChannel channel = view_as<DiscordChannel>(json_object_get(hObject, "channel"));
//Handle fwd = view_as<Handle>(json_object_get(hObject, "callback"));
char channelID[32];
channel.GetID(channelID, sizeof(channelID));
char lastMessage[64];
channel.GetLastMessageID(lastMessage, sizeof(lastMessage));
char url[256];
FormatEx(url, sizeof(url), "channels/%s/messages?limit=%i&after=%s", channelID, 100, lastMessage);
Handle request = PrepareRequest(bot, url, _, null, OnGetMessage);
if(request == null) {
delete bot;
delete channel;
CreateTimer(2.0, GetMessagesDelayed, hObject);
return;
}
char route[128];
FormatEx(route, sizeof(route), "channels/%s", channelID);
SteamWorks_SetHTTPRequestContextValue(request, hObject, UrlToDP(route));
delete bot;
delete channel;
DiscordSendRequest(request, route);
}
public Action GetMessagesDelayed(Handle timer, any data) {
GetMessages(view_as<Handle>(data));
}
public Action CheckMessageTimer(Handle timer, any dpt) {
GetMessages(view_as<Handle>(dpt));
}
public int OnGetMessage(Handle request, bool failure, int offset, int statuscode, any dp) {
if(failure || statuscode != 200) {
if(statuscode == 429 || statuscode == 500) {
GetMessages(view_as<Handle>(dp));
delete request;
return;
}
LogError("[DISCORD] Couldn't Retrieve Messages - Fail %i %i", failure, statuscode);
delete request;
Handle fwd = view_as<Handle>(JsonObjectGetInt(view_as<Handle>(dp), "callback"));
if(fwd != null) delete fwd;
delete view_as<Handle>(dp);
return;
}
SteamWorks_GetHTTPResponseBodyCallback(request, OnGetMessage_Data, dp);
delete request;
}
public int OnGetMessage_Data(const char[] data, any dpt) {
Handle hObj = view_as<Handle>(dpt);
DiscordBot Bot = view_as<DiscordBot>(json_object_get(hObj, "bot"));
DiscordChannel channel = view_as<DiscordChannel>(json_object_get(hObj, "channel"));
Handle fwd = view_as<Handle>(JsonObjectGetInt(hObj, "callback"));
if(!Bot.IsListeningToChannel(channel) || GetForwardFunctionCount(fwd) == 0) {
delete Bot;
delete channel;
delete hObj;
delete fwd;
return;
}
Handle hJson = json_load(data);
if(json_is_array(hJson)) {
for(int i = json_array_size(hJson) - 1; i >= 0; i--) {
Handle hObject = json_array_get(hJson, i);
//The reason we find Channel for each message instead of global incase
//Bot stops listening for the channel while we are still sending messages
char channelID[32];
JsonObjectGetString(hObject, "channel_id", channelID, sizeof(channelID));
//Find Channel corresponding to Channel id
//DiscordChannel Channel = Bot.GetListeningChannelByID(channelID);
if(!Bot.IsListeningToChannelID(channelID)) {
//Channel is no longer listed to, remove any handles & stop
delete hObject;
delete hJson;
delete fwd;
delete Bot;
delete channel;
delete hObj;
return;
}
char id[32];
JsonObjectGetString(hObject, "id", id, sizeof(id));
if(i == 0) {
channel.SetLastMessageID(id);
}
//Get info and fire forward
if(fwd != null) {
Call_StartForward(fwd);
Call_PushCell(Bot);
Call_PushCell(channel);
Call_PushCell(view_as<DiscordMessage>(hObject));
Call_Finish();
}
delete hObject;
}
}
CreateTimer(Bot.MessageCheckInterval, CheckMessageTimer, hObj);
delete Bot;
delete channel;
delete hJson;
}

View File

@@ -0,0 +1,36 @@
public int Native_DiscordMessage_GetID(Handle plugin, int numParams) {
Handle hJson = GetNativeCell(1);
char buffer[64];
JsonObjectGetString(hJson, "id", buffer, sizeof(buffer));
SetNativeString(2, buffer, GetNativeCell(3));
}
public int Native_DiscordMessage_IsPinned(Handle plugin, int numParams) {
Handle hJson = GetNativeCell(1);
return JsonObjectGetBool(hJson, "pinned");
}
public int Native_DiscordMessage_GetAuthor(Handle plugin, int numParams) {
Handle hJson = GetNativeCell(1);
Handle hAuthor = json_object_get(hJson, "author");
DiscordUser user = view_as<DiscordUser>(CloneHandle(hAuthor, plugin));
delete hAuthor;
return _:user;
}
public int Native_DiscordMessage_GetContent(Handle plugin, int numParams) {
Handle hJson = GetNativeCell(1);
static char buffer[2000];
JsonObjectGetString(hJson, "content", buffer, sizeof(buffer));
SetNativeString(2, buffer, GetNativeCell(3));
}
public int Native_DiscordMessage_GetChannelID(Handle plugin, int numParams) {
Handle hJson = GetNativeCell(1);
char buffer[64];
JsonObjectGetString(hJson, "channel_id", buffer, sizeof(buffer));
SetNativeString(2, buffer, GetNativeCell(3));
}

View File

@@ -0,0 +1,138 @@
public int Native_DiscordBot_SendMessageToChannel(Handle plugin, int numParams) {
DiscordBot bot = GetNativeCell(1);
char channel[32];
static char message[2048];
GetNativeString(2, channel, sizeof(channel));
GetNativeString(3, message, sizeof(message));
Function fCallback = GetNativeCell(4);
any data = GetNativeCell(5);
Handle fForward = null;
if(fCallback != INVALID_FUNCTION) {
fForward = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
AddToForward(fForward, plugin, fCallback);
}
SendMessage(bot, channel, message, fForward, data);
}
public int Native_DiscordBot_SendMessage(Handle plugin, int numParams) {
DiscordBot bot = GetNativeCell(1);
DiscordChannel Channel = GetNativeCell(2);
char channelID[32];
Channel.GetID(channelID, sizeof(channelID));
static char message[2048];
GetNativeString(3, message, sizeof(message));
Function fCallback = GetNativeCell(4);
any data = GetNativeCell(5);
Handle fForward = null;
if(fCallback != INVALID_FUNCTION) {
fForward = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
AddToForward(fForward, plugin, fCallback);
}
SendMessage(bot, channelID, message, fForward, data);
}
public int Native_DiscordChannel_SendMessage(Handle plugin, int numParams) {
DiscordChannel channel = view_as<DiscordChannel>(GetNativeCell(1));
char channelID[32];
channel.GetID(channelID, sizeof(channelID));
DiscordBot bot = GetNativeCell(2);
static char message[2048];
GetNativeString(3, message, sizeof(message));
Function fCallback = GetNativeCell(4);
any data = GetNativeCell(5);
Handle fForward = null;
if(fCallback != INVALID_FUNCTION) {
fForward = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
AddToForward(fForward, plugin, fCallback);
}
SendMessage(bot, channelID, message, fForward, data);
}
static void SendMessage(DiscordBot bot, char[] channel, char[] message, Handle fForward, any data) {
Handle hJson = json_object();
json_object_set_new(hJson, "content", json_string(message));
char url[64];
FormatEx(url, sizeof(url), "channels/%s/messages", channel);
DataPack dpSafety = new DataPack();
WritePackCell(dpSafety, bot);
WritePackString(dpSafety, channel);
WritePackString(dpSafety, message);
WritePackCell(dpSafety, fForward);
WritePackCell(dpSafety, data);
Handle request = PrepareRequest(bot, url, k_EHTTPMethodPOST, hJson, GetSendMessageData);
if(request == null) {
delete hJson;
CreateTimer(2.0, SendMessageDelayed, dpSafety);
return;
}
SteamWorks_SetHTTPRequestContextValue(request, dpSafety, UrlToDP(url));
DiscordSendRequest(request, url);
}
public Action SendMessageDelayed(Handle timer, any data) {
DataPack dp = view_as<DataPack>(data);
ResetPack(dp);
DiscordBot bot = ReadPackCell(dp);
char channel[32];
ReadPackString(dp, channel, sizeof(channel));
char message[2048];
ReadPackString(dp, message, sizeof(message));
Handle fForward = ReadPackCell(dp);
any dataa = ReadPackCell(dp);
delete dp;
SendMessage(bot, channel, message, fForward, dataa);
}
public int GetSendMessageData(Handle request, bool failure, int offset, int statuscode, any dp) {
if(failure || statuscode != 200) {
if(statuscode == 429 || statuscode == 500) {
ResetPack(dp);
DiscordBot bot = ReadPackCell(dp);
char channel[32];
ReadPackString(dp, channel, sizeof(channel));
char message[2048];
ReadPackString(dp, message, sizeof(message));
Handle fForward = ReadPackCell(dp);
any data = ReadPackCell(dp);
delete view_as<Handle>(dp);
SendMessage(bot, channel, message, fForward, data);
delete request;
return;
}
LogError("[DISCORD] Couldn't Send Message - Fail %i %i", failure, statuscode);
delete request;
delete view_as<Handle>(dp);
return;
}
delete request;
delete view_as<Handle>(dp);
}

View File

@@ -0,0 +1,104 @@
public int Native_DiscordWebHook_Send(Handle plugin, int numParams) {
DiscordWebHook hook = GetNativeCell(1);
SendWebHook(view_as<DiscordWebHook>(hook));
}
public void SendWebHook(DiscordWebHook hook) {
if(!JsonObjectGetBool(hook, "__selfCopy", false)) {
hook = view_as<DiscordWebHook>(json_deep_copy(hook));
json_object_set_new(hook, "__selfCopy", json_true());
}
Handle hJson = hook.Data;
char url[256];
hook.GetUrl(url, sizeof(url));
if(hook.SlackMode) {
if(StrContains(url, "/slack") == -1) {
Format(url, sizeof(url), "%s/slack", url);
}
RenameJsonObject(hJson, "content", "text");
RenameJsonObject(hJson, "embeds", "attachments");
Handle hAttachments = json_object_get(hJson, "attachments");
if(hAttachments != null) {
if(json_is_array(hAttachments)) {
for(int i = 0; i < json_array_size(hAttachments); i++) {
Handle hEmbed = json_array_get(hAttachments, i);
Handle hFields = json_object_get(hEmbed, "fields");
if(hFields) {
if(json_is_array(hFields)) {
for(int j = 0; j < json_array_size(hFields); j++) {
Handle hField = json_array_get(hFields, j);
RenameJsonObject(hField, "name", "title");
RenameJsonObject(hField, "inline", "short");
//json_array_set_new(hFields, j, hField);
delete hField;
}
}
//json_object_set_new(hEmbed, "fields", hFields);
delete hFields;
}
//json_array_set_new(hAttachments, i, hEmbed);
delete hEmbed;
}
}
//json_object_set_new(hJson, "attachments", hAttachments);
delete hAttachments;
}
}
//Send
DiscordRequest request = new DiscordRequest(url, k_EHTTPMethodPOST);
request.SetCallbacks(HTTPCompleted, SendWebHookReceiveData);
request.SetJsonBodyEx(hJson);
//Handle request = PrepareRequestRaw(null, url, k_EHTTPMethodPOST, hJson, SendWebHookReceiveData);
if(request == null) {
CreateTimer(2.0, SendWebHookDelayed, hJson);
return;
}
request.SetContextValue(hJson, UrlToDP(url));
//DiscordSendRequest(request, url);
request.Send(url);
}
public Action SendWebHookDelayed(Handle timer, any data) {
SendWebHook(view_as<DiscordWebHook>(data));
}
public SendWebHookReceiveData(Handle request, bool failure, int offset, int statuscode, any dp) {
if(failure || (statuscode != 200 && statuscode != 204)) {
if(statuscode == 400) {
PrintToServer("BAD REQUEST");
SteamWorks_GetHTTPResponseBodyCallback(request, WebHookData, dp);
}
if(statuscode == 429 || statuscode == 500) {
SendWebHook(view_as<DiscordWebHook>(dp));
delete request;
return;
}
LogError("[DISCORD] Couldn't Send Webhook - Fail %i %i", failure, statuscode);
delete request;
delete view_as<Handle>(dp);
return;
}
delete request;
delete view_as<Handle>(dp);
}
public int WebHookData(const char[] data, any dp) {
PrintToServer("DATA RECE: %s", data);
static char stringJson[16384];
stringJson[0] = '\0';
json_dump(view_as<Handle>(dp), stringJson, sizeof(stringJson), 0, true);
PrintToServer("DATA SENT: %s", stringJson);
}

View File

@@ -0,0 +1,44 @@
public int Native_DiscordUser_GetID(Handle plugin, int numParams) {
Handle hJson = GetNativeCell(1);
char buffer[64];
JsonObjectGetString(hJson, "id", buffer, sizeof(buffer));
SetNativeString(2, buffer, GetNativeCell(3));
}
public int Native_DiscordUser_GetUsername(Handle plugin, int numParams) {
Handle hJson = GetNativeCell(1);
char buffer[64];
JsonObjectGetString(hJson, "username", buffer, sizeof(buffer));
SetNativeString(2, buffer, GetNativeCell(3));
}
public int Native_DiscordUser_GetDiscriminator(Handle plugin, int numParams) {
Handle hJson = GetNativeCell(1);
char buffer[16];
JsonObjectGetString(hJson, "discriminator", buffer, sizeof(buffer));
SetNativeString(2, buffer, GetNativeCell(3));
}
public int Native_DiscordUser_GetAvatar(Handle plugin, int numParams) {
Handle hJson = GetNativeCell(1);
char buffer[256];
JsonObjectGetString(hJson, "avatar", buffer, sizeof(buffer));
SetNativeString(2, buffer, GetNativeCell(3));
}
public int Native_DiscordUser_GetEmail(Handle plugin, int numParams) {
Handle hJson = GetNativeCell(1);
char buffer[64];
JsonObjectGetString(hJson, "email", buffer, sizeof(buffer));
SetNativeString(2, buffer, GetNativeCell(3));
}
public int Native_DiscordUser_IsVerified(Handle plugin, int numParams) {
Handle hJson = GetNativeCell(1);
return JsonObjectGetBool(hJson, "verified");
}
public int Native_DiscordUser_IsBot(Handle plugin, int numParams) {
Handle hJson = GetNativeCell(1);
return JsonObjectGetBool(hJson, "bot");
}

View File

@@ -0,0 +1,129 @@
public int Native_DiscordBot_DeleteMessageID(Handle plugin, int numParams) {
DiscordBot bot = GetNativeCell(1);
char channelid[64];
GetNativeString(2, channelid, sizeof(channelid));
char msgid[64];
GetNativeString(3, msgid, sizeof(msgid));
Function fCallback = GetNativeCell(4);
any data = GetNativeCell(5);
DataPack dp = CreateDataPack();
WritePackCell(dp, bot);
WritePackString(dp, channelid);
WritePackString(dp, msgid);
WritePackCell(dp, plugin);
WritePackFunction(dp, fCallback);
WritePackCell(dp, data);
ThisDeleteMessage(bot, channelid, msgid, dp);
}
public int Native_DiscordBot_DeleteMessage(Handle plugin, int numParams) {
DiscordBot bot = GetNativeCell(1);
char channelid[64];
DiscordChannel channel = GetNativeCell(2);
channel.GetID(channelid, sizeof(channelid));
char msgid[64];
DiscordMessage msg = GetNativeCell(3);
msg.GetID(msgid, sizeof(msgid));
Function fCallback = GetNativeCell(4);
any data = GetNativeCell(5);
DataPack dp = CreateDataPack();
WritePackCell(dp, bot);
WritePackString(dp, channelid);
WritePackString(dp, msgid);
WritePackCell(dp, plugin);
WritePackFunction(dp, fCallback);
WritePackCell(dp, data);
ThisDeleteMessage(bot, channelid, msgid, dp);
}
static void ThisDeleteMessage(DiscordBot bot, char[] channelid, char[] msgid, DataPack dp) {
char url[64];
FormatEx(url, sizeof(url), "channels/%s/messages/%s", channelid, msgid);
Handle request = PrepareRequest(bot, url, k_EHTTPMethodDELETE, null, MessageDeletedResp);
if(request == null) {
CreateTimer(2.0, ThisDeleteMessageDelayed, dp);
return;
}
SteamWorks_SetHTTPRequestContextValue(request, dp, UrlToDP(url));
DiscordSendRequest(request, url);
}
public Action ThisDeleteMessageDelayed(Handle timer, any data) {
DataPack dp = view_as<DataPack>(data);
ResetPack(dp);
DiscordBot bot = ReadPackCell(dp);
char channelid[32];
ReadPackString(dp, channelid, sizeof(channelid));
char msgid[32];
ReadPackString(dp, msgid, sizeof(msgid));
ThisDeleteMessage(bot, channelid, msgid, dp);
}
public int MessageDeletedResp(Handle request, bool failure, int offset, int statuscode, any dp) {
if(failure || statuscode != 204) {
if(statuscode == 429 || statuscode == 500) {
ResetPack(dp);
DiscordBot bot = ReadPackCell(dp);
char channelid[32];
ReadPackString(dp, channelid, sizeof(channelid));
char msgid[32];
ReadPackString(dp, msgid, sizeof(msgid));
ThisDeleteMessage(bot, channelid, msgid, view_as<DataPack>(dp));
delete request;
return;
}
LogError("[DISCORD] Couldn't delete message - Fail %i %i", failure, statuscode);
delete request;
delete view_as<Handle>(dp);
return;
}
ResetPack(dp);
DiscordBot bot = ReadPackCell(dp);
char channelid[32];
ReadPackString(dp, channelid, sizeof(channelid));
char msgid[32];
ReadPackString(dp, msgid, sizeof(msgid));
Handle plugin = view_as<Handle>(ReadPackCell(dp));
Function func = ReadPackFunction(dp);
any pluginData = ReadPackCell(dp);
Handle fForward = INVALID_HANDLE;
if(func != INVALID_FUNCTION) {
fForward = CreateForward(ET_Ignore, Param_Cell, Param_Cell);
AddToForward(fForward, plugin, func);
Call_StartForward(fForward);
Call_PushCell(bot);
Call_PushCell(pluginData);
Call_Finish();
delete fForward;
}
delete view_as<Handle>(dp);
delete request;
}

View File

@@ -0,0 +1,328 @@
public int Native_DiscordBot_AddReaction(Handle plugin, int numParams) {
DiscordBot bot = GetNativeCell(1);
char channel[32];
GetNativeString(2, channel, sizeof(channel));
char msgid[64];
GetNativeString(3, msgid, sizeof(msgid));
char emoji[128];
GetNativeString(4, emoji, sizeof(emoji));
AddReaction(bot, channel, msgid, emoji);
}
public int Native_DiscordBot_DeleteReaction(Handle plugin, int numParams) {
DiscordBot bot = GetNativeCell(1);
char channel[32];
GetNativeString(2, channel, sizeof(channel));
char msgid[64];
GetNativeString(3, msgid, sizeof(msgid));
char emoji[128];
GetNativeString(4, emoji, sizeof(emoji));
char user[128];
GetNativeString(5, user, sizeof(user));
DeleteReaction(bot, channel, msgid, emoji, user);
}
public int Native_DiscordBot_GetReaction(Handle plugin, int numParams) {
DiscordBot bot = GetNativeCell(1);
char channel[32];
GetNativeString(2, channel, sizeof(channel));
char msgid[64];
GetNativeString(3, msgid, sizeof(msgid));
char emoji[128];
GetNativeString(4, emoji, sizeof(emoji));
OnGetReactions fCallback = GetNativeCell(5);
Handle fForward = null;
if(fCallback != INVALID_FUNCTION) {
fForward = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_String, Param_String, Param_String, Param_String, Param_Cell);
AddToForward(fForward, plugin, fCallback);
}
any data = GetNativeCell(6);
GetReaction(bot, channel, msgid, emoji, fForward, data);
}
///channels/{channel.id}/messages/{message.id}/reactions/{emoji}/@me
public void AddReaction(DiscordBot bot, char[] channel, char[] messageid, char[] emoji) {
char url[256];
FormatEx(url, sizeof(url), "channels/%s/messages/%s/reactions/%s/@me", channel, messageid, emoji);
Handle request = PrepareRequest(bot, url, k_EHTTPMethodPUT, null, AddReactionReceiveData);
DataPack dp = new DataPack();
WritePackCell(dp, bot);
WritePackString(dp, channel);
WritePackString(dp, messageid);
WritePackString(dp, emoji);
if(request == dp) {
CreateTimer(2.0, AddReactionDelayed, dp);
return;
}
char route[128];
FormatEx(route, sizeof(route), "channels/%s/messages/msgid/reactions", channel);
SteamWorks_SetHTTPRequestContextValue(request, dp, UrlToDP(route));
DiscordSendRequest(request, url);
}
public Action AddReactionDelayed(Handle timer, any data) {
DataPack dp = view_as<DataPack>(data);
DiscordBot bot = ReadPackCell(dp);
char channel[64];
char messageid[64];
char emoji[64];
ReadPackString(dp, channel, sizeof(channel));
ReadPackString(dp, messageid, sizeof(messageid));
ReadPackString(dp, emoji, sizeof(emoji));
delete dp;
AddReaction(bot, channel, messageid, emoji);
}
public AddReactionReceiveData(Handle request, bool failure, int offset, int statuscode, any data) {
if(failure || statuscode != 204) {
if(statuscode == 429 || statuscode == 500) {
DataPack dp = view_as<DataPack>(data);
DiscordBot bot = ReadPackCell(dp);
char channel[64];
char messageid[64];
char emoji[64];
ReadPackString(dp, channel, sizeof(channel));
ReadPackString(dp, messageid, sizeof(messageid));
ReadPackString(dp, emoji, sizeof(emoji));
delete dp;
AddReaction(bot, channel, messageid, emoji);
delete request;
return;
}
LogError("[DISCORD] Couldn't Add Reaction - Fail %i %i", failure, statuscode);
delete request;
delete view_as<Handle>(data);
delete view_as<Handle>(data);
return;
}
delete request;
delete view_as<Handle>(data);
}
///channels/{channel.id}/messages/{message.id}/reactions/{emoji}/{user.id}
public void DeleteReaction(DiscordBot bot, char[] channel, char[] messageid, char[] emoji, char[] userid) {
char url[256];
if(StrEqual(userid, "@all")) {
FormatEx(url, sizeof(url), "channels/%s/messages/%s/reactions/%s", channel, messageid, emoji);
}else {
FormatEx(url, sizeof(url), "channels/%s/messages/%s/reactions/%s/%s", channel, messageid, emoji, userid);
}
Handle request = PrepareRequest(bot, url, k_EHTTPMethodDELETE, null, DeleteReactionReceiveData);
DataPack dp = new DataPack();
WritePackCell(dp, bot);
WritePackString(dp, channel);
WritePackString(dp, messageid);
WritePackString(dp, emoji);
WritePackString(dp, userid);
if(request == dp) {
CreateTimer(2.0, DeleteReactionDelayed, dp);
return;
}
char route[128];
FormatEx(route, sizeof(route), "channels/%s/messages/msgid/reactions", channel);
SteamWorks_SetHTTPRequestContextValue(request, dp, UrlToDP(route));
DiscordSendRequest(request, url);
}
public Action DeleteReactionDelayed(Handle timer, any data) {
DataPack dp = view_as<DataPack>(data);
DiscordBot bot = ReadPackCell(dp);
char channel[64];
char messageid[64];
char emoji[64];
char userid[64];
ReadPackString(dp, channel, sizeof(channel));
ReadPackString(dp, messageid, sizeof(messageid));
ReadPackString(dp, emoji, sizeof(emoji));
ReadPackString(dp, userid, sizeof(userid));
delete dp;
DeleteReaction(bot, channel, messageid, emoji, userid);
}
public DeleteReactionReceiveData(Handle request, bool failure, int offset, int statuscode, any data) {
if(failure || statuscode != 204) {
if(statuscode == 429 || statuscode == 500) {
DataPack dp = view_as<DataPack>(data);
DiscordBot bot = ReadPackCell(dp);
char channel[64];
char messageid[64];
char emoji[64];
char userid[64];
ReadPackString(dp, channel, sizeof(channel));
ReadPackString(dp, messageid, sizeof(messageid));
ReadPackString(dp, emoji, sizeof(emoji));
ReadPackString(dp, userid, sizeof(userid));
delete dp;
DeleteReaction(bot, channel, messageid, emoji, userid);
delete request;
return;
}
LogError("[DISCORD] Couldn't Delete Reaction - Fail %i %i", failure, statuscode);
delete request;
delete view_as<Handle>(data);
return;
}
delete request;
delete view_as<Handle>(data);
}
public void GetReaction(DiscordBot bot, char[] channel, char[] messageid, char[] emoji, Handle fForward, any data) {
char url[256];
FormatEx(url, sizeof(url), "channels/%s/messages/%s/reactions/%s", channel, messageid, emoji);
Handle request = PrepareRequest(bot, url, k_EHTTPMethodGET, null, GetReactionReceiveData);
DataPack dp = new DataPack();
WritePackCell(dp, bot);
WritePackString(dp, channel);
WritePackString(dp, messageid);
WritePackString(dp, emoji);
WritePackCell(dp, fForward);
WritePackCell(dp, data);
if(request == dp) {
CreateTimer(2.0, GetReactionDelayed, dp);
return;
}
char route[128];
FormatEx(route, sizeof(route), "channels/%s/messages/msgid/reactions", channel);
SteamWorks_SetHTTPRequestContextValue(request, dp, UrlToDP(route));
DiscordSendRequest(request, url);
}
public Action GetReactionDelayed(Handle timer, any data) {
DataPack dp = view_as<DataPack>(data);
DiscordBot bot = ReadPackCell(dp);
char channel[64];
char messageid[64];
char emoji[64];
ReadPackString(dp, channel, sizeof(channel));
ReadPackString(dp, messageid, sizeof(messageid));
ReadPackString(dp, emoji, sizeof(emoji));
Handle fForward = ReadPackCell(dp);
any addData = ReadPackCell(dp);
delete dp;
GetReaction(bot, channel, messageid, emoji, fForward, addData);
}
public GetReactionReceiveData(Handle request, bool failure, int offset, int statuscode, any data) {
if(failure || statuscode != 204) {
if(statuscode == 429 || statuscode == 500) {
DataPack dp = view_as<DataPack>(data);
DiscordBot bot = ReadPackCell(dp);
char channel[64];
char messageid[64];
char emoji[64];
ReadPackString(dp, channel, sizeof(channel));
ReadPackString(dp, messageid, sizeof(messageid));
ReadPackString(dp, emoji, sizeof(emoji));
Handle fForward = ReadPackCell(dp);
any addData = ReadPackCell(dp);
delete dp;
GetReaction(bot, channel, messageid, emoji, fForward, addData);
delete request;
return;
}
LogError("[DISCORD] Couldn't Delete Reaction - Fail %i %i", failure, statuscode);
delete request;
delete view_as<Handle>(data);
return;
}
SteamWorks_GetHTTPResponseBodyCallback(request, GetReactionsData, data);
delete request;
}
public int GetReactionsData(const char[] data, any datapack) {
DataPack dp = view_as<DataPack>(datapack);
DiscordBot bot = ReadPackCell(dp);
char channel[64];
char messageid[64];
char emoji[64];
ReadPackString(dp, channel, sizeof(channel));
ReadPackString(dp, messageid, sizeof(messageid));
ReadPackString(dp, emoji, sizeof(emoji));
Handle fForward = ReadPackCell(dp);
any addData = ReadPackCell(dp);
delete dp;
Handle hJson = json_load(data);
ArrayList alUsers = new ArrayList();
if(json_is_array(hJson)) {
for(int i = 0; i < json_array_size(hJson); i++) {
DiscordUser user = view_as<DiscordUser>(json_array_get(hJson, i));
alUsers.Push(user);
}
}
delete hJson;
if(fForward != null) {
Call_StartForward(fForward);
Call_PushCell(bot);
Call_PushCell(alUsers);
Call_PushString(channel);
Call_PushString(messageid);
Call_PushString(emoji);
Call_PushCell(addData);
Call_Finish();
}
for(int i = 0; i < alUsers.Length; i++) {
DiscordUser user = alUsers.Get(i);
delete user;
}
delete alUsers;
}

View File

@@ -0,0 +1,313 @@
#pragma semicolon 1
#define PLUGIN_VERSION "0.1.103"
#include <sourcemod>
#include <discord>
#include <SteamWorks>
#include <smjansson>
#include "discord/DiscordRequest.sp"
#include "discord/SendMessage.sp"
#include "discord/GetGuilds.sp"
#include "discord/GetGuildChannels.sp"
#include "discord/ListenToChannel.sp"
#include "discord/SendWebHook.sp"
#include "discord/reactions.sp"
#include "discord/UserObject.sp"
#include "discord/MessageObject.sp"
#include "discord/GuildMembers.sp"
#include "discord/GuildRole.sp"
#include "discord/deletemessage.sp"
//For rate limitation
Handle hRateLimit = null;
Handle hRateReset = null;
Handle hRateLeft = null;
public Plugin myinfo = {
name = "Discord API",
author = "Deathknife",
description = "",
version = PLUGIN_VERSION,
url = ""
};
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
CreateNative("DiscordBot.GetToken", Native_DiscordBot_Token_Get);
//SendMessage.sp
CreateNative("DiscordBot.SendMessage", Native_DiscordBot_SendMessage);
CreateNative("DiscordBot.SendMessageToChannelID", Native_DiscordBot_SendMessageToChannel);
CreateNative("DiscordChannel.SendMessage", Native_DiscordChannel_SendMessage);
//deletemessage.sp
CreateNative("DiscordBot.DeleteMessageID", Native_DiscordBot_DeleteMessageID);
CreateNative("DiscordBot.DeleteMessage", Native_DiscordBot_DeleteMessage);
//ListenToChannel.sp
CreateNative("DiscordBot.StartTimer", Native_DiscordBot_StartTimer);
//GetGuilds.sp
CreateNative("DiscordBot.GetGuilds", Native_DiscordBot_GetGuilds);
//GetGuildChannels.sp
CreateNative("DiscordBot.GetGuildChannels", Native_DiscordBot_GetGuildChannels);
//GuildRole.sp
CreateNative("DiscordBot.GetGuildRoles", Native_DiscordBot_GetGuildRoles);
//reactions.sp
CreateNative("DiscordBot.AddReactionID", Native_DiscordBot_AddReaction);
CreateNative("DiscordBot.DeleteReactionID", Native_DiscordBot_DeleteReaction);
CreateNative("DiscordBot.GetReactionID", Native_DiscordBot_GetReaction);
//GuildMembers.sp
CreateNative("DiscordBot.GetGuildMembers", Native_DiscordBot_GetGuildMembers);
CreateNative("DiscordBot.GetGuildMembersAll", Native_DiscordBot_GetGuildMembersAll);
//CreateNative("DiscordChannel.Destroy", Native_DiscordChannel_Destroy);
//SendWebHook.sp
CreateNative("DiscordWebHook.Send", Native_DiscordWebHook_Send);
//CreateNative("DiscordWebHook.AddField", Native_DiscordWebHook_AddField);
//CreateNative("DiscordWebHook.DeleteFields", Native_DiscordWebHook_DeleteFields);
//UserObject.sp
CreateNative("DiscordUser.GetID", Native_DiscordUser_GetID);
CreateNative("DiscordUser.GetUsername", Native_DiscordUser_GetUsername);
CreateNative("DiscordUser.GetDiscriminator", Native_DiscordUser_GetDiscriminator);
CreateNative("DiscordUser.GetAvatar", Native_DiscordUser_GetAvatar);
CreateNative("DiscordUser.IsVerified", Native_DiscordUser_IsVerified);
CreateNative("DiscordUser.GetEmail", Native_DiscordUser_GetEmail);
CreateNative("DiscordUser.IsBot", Native_DiscordUser_IsBot);
//MessageObject.sp
CreateNative("DiscordMessage.GetID", Native_DiscordMessage_GetID);
CreateNative("DiscordMessage.IsPinned", Native_DiscordMessage_IsPinned);
CreateNative("DiscordMessage.GetAuthor", Native_DiscordMessage_GetAuthor);
CreateNative("DiscordMessage.GetContent", Native_DiscordMessage_GetContent);
CreateNative("DiscordMessage.GetChannelID", Native_DiscordMessage_GetChannelID);
RegPluginLibrary("discord-api");
return APLRes_Success;
}
public void OnPluginStart() {
hRateLeft = new StringMap();
hRateReset = new StringMap();
hRateLimit = new StringMap();
}
public int Native_DiscordBot_Token_Get(Handle plugin, int numParams) {
DiscordBot bot = GetNativeCell(1);
static char token[196];
JsonObjectGetString(bot, "token", token, sizeof(token));
SetNativeString(2, token, GetNativeCell(3));
}
stock void BuildAuthHeader(Handle request, DiscordBot Bot) {
static char buffer[256];
static char token[196];
JsonObjectGetString(Bot, "token", token, sizeof(token));
FormatEx(buffer, sizeof(buffer), "Bot %s", token);
SteamWorks_SetHTTPRequestHeaderValue(request, "Authorization", buffer);
}
stock Handle PrepareRequest(DiscordBot bot, char[] url, EHTTPMethod method=k_EHTTPMethodGET, Handle hJson=null, SteamWorksHTTPDataReceived DataReceived = INVALID_FUNCTION, SteamWorksHTTPRequestCompleted RequestCompleted = INVALID_FUNCTION) {
static char stringJson[16384];
stringJson[0] = '\0';
if(hJson != null) {
json_dump(hJson, stringJson, sizeof(stringJson), 0, true);
}
//Format url
static char turl[128];
FormatEx(turl, sizeof(turl), "https://discordapp.com/api/%s", url);
Handle request = SteamWorks_CreateHTTPRequest(method, turl);
if(request == null) {
return null;
}
if(bot != null) {
BuildAuthHeader(request, bot);
}
SteamWorks_SetHTTPRequestRawPostBody(request, "application/json; charset=UTF-8", stringJson, strlen(stringJson));
SteamWorks_SetHTTPRequestNetworkActivityTimeout(request, 30);
if(RequestCompleted == INVALID_FUNCTION) {
//I had some bugs previously where it wouldn't send request and return code 0 if I didn't set request completed.
//This is just a safety then, my issue could have been something else and I will test more later on
RequestCompleted = HTTPCompleted;
}
if(DataReceived == INVALID_FUNCTION) {
//Need to close the request handle
DataReceived = HTTPDataReceive;
}
SteamWorks_SetHTTPCallbacks(request, RequestCompleted, HeadersReceived, DataReceived);
if(hJson != null) delete hJson;
return request;
}
stock Handle PrepareRequestRaw(DiscordBot bot, char[] url, EHTTPMethod method=k_EHTTPMethodGET, Handle hJson=null, SteamWorksHTTPDataReceived DataReceived = INVALID_FUNCTION, SteamWorksHTTPRequestCompleted RequestCompleted = INVALID_FUNCTION) {
static char stringJson[16384];
stringJson[0] = '\0';
if(hJson != null) {
json_dump(hJson, stringJson, sizeof(stringJson), 0, true);
}
Handle request = SteamWorks_CreateHTTPRequest(method, url);
if(request == null) {
return null;
}
if(bot != null) {
BuildAuthHeader(request, bot);
}
SteamWorks_SetHTTPRequestRawPostBody(request, "application/json; charset=UTF-8", stringJson, strlen(stringJson));
SteamWorks_SetHTTPRequestNetworkActivityTimeout(request, 30);
if(RequestCompleted == INVALID_FUNCTION) {
//I had some bugs previously where it wouldn't send request and return code 0 if I didn't set request completed.
//This is just a safety then, my issue could have been something else and I will test more later on
RequestCompleted = HTTPCompleted;
}
if(DataReceived == INVALID_FUNCTION) {
//Need to close the request handle
DataReceived = HTTPDataReceive;
}
SteamWorks_SetHTTPCallbacks(request, RequestCompleted, HeadersReceived, DataReceived);
if(hJson != null) delete hJson;
return request;
}
public int HTTPCompleted(Handle request, bool failure, bool requestSuccessful, EHTTPStatusCode statuscode, any data, any data2) {
}
public int HTTPDataReceive(Handle request, bool failure, int offset, int statuscode, any dp) {
delete request;
}
public int HeadersReceived(Handle request, bool failure, any data, any datapack) {
DataPack dp = view_as<DataPack>(datapack);
if(failure) {
delete dp;
return;
}
char xRateLimit[16];
char xRateLeft[16];
char xRateReset[32];
bool exists = false;
exists = SteamWorks_GetHTTPResponseHeaderValue(request, "X-RateLimit-Limit", xRateLimit, sizeof(xRateLimit));
exists = SteamWorks_GetHTTPResponseHeaderValue(request, "X-RateLimit-Remaining", xRateLeft, sizeof(xRateLeft));
exists = SteamWorks_GetHTTPResponseHeaderValue(request, "X-RateLimit-Reset", xRateReset, sizeof(xRateReset));
//Get url
char route[128];
ResetPack(dp);
ReadPackString(dp, route, sizeof(route));
delete dp;
int reset = StringToInt(xRateReset);
if(reset > GetTime() + 3) {
reset = GetTime() + 3;
}
if(exists) {
SetTrieValue(hRateReset, route, reset);
SetTrieValue(hRateLeft, route, StringToInt(xRateLeft));
SetTrieValue(hRateLimit, route, StringToInt(xRateLimit));
}else {
SetTrieValue(hRateReset, route, -1);
SetTrieValue(hRateLeft, route, -1);
SetTrieValue(hRateLimit, route, -1);
}
}
/*
This is rate limit imposing for per-route basis. Doesn't support global limit yet.
*/
public void DiscordSendRequest(Handle request, const char[] route) {
//Check for reset
int time = GetTime();
int resetTime;
int defLimit = 0;
if(!GetTrieValue(hRateLimit, route, defLimit)) {
defLimit = 1;
}
bool exists = GetTrieValue(hRateReset, route, resetTime);
if(!exists) {
SetTrieValue(hRateReset, route, GetTime() + 5);
SetTrieValue(hRateLeft, route, defLimit - 1);
SteamWorks_SendHTTPRequest(request);
return;
}
if(time == -1) {
//No x-rate-limit send
SteamWorks_SendHTTPRequest(request);
return;
}
if(time > resetTime) {
SetTrieValue(hRateLeft, route, defLimit - 1);
SteamWorks_SendHTTPRequest(request);
return;
}else {
int left;
GetTrieValue(hRateLeft, route, left);
if(left == 0) {
float remaining = float(resetTime) - float(time) + 1.0;
Handle dp = new DataPack();
WritePackCell(dp, request);
WritePackString(dp, route);
CreateTimer(remaining, SendRequestAgain, dp);
}else {
left--;
SetTrieValue(hRateLeft, route, left);
SteamWorks_SendHTTPRequest(request);
}
}
}
public Handle UrlToDP(char[] url) {
DataPack dp = new DataPack();
WritePackString(dp, url);
return dp;
}
public Action SendRequestAgain(Handle timer, any dp) {
ResetPack(dp);
Handle request = ReadPackCell(dp);
char route[128];
ReadPackString(dp, route, sizeof(route));
delete view_as<Handle>(dp);
DiscordSendRequest(request, route);
}
stock bool RenameJsonObject(Handle hJson, char[] key, char[] toKey) {
Handle hObject = json_object_get(hJson, key);
if(hObject != null) {
json_object_set_new(hJson, toKey, hObject);
json_object_del(hJson, key);
return true;
}
return false;
}

View File

@@ -0,0 +1,209 @@
#pragma semicolon 1
#define PLUGIN_VERSION "1.10"
#include <sourcemod>
#include <discord>
#define BOT_TOKEN ""
#define WEBHOOK ""
public Plugin myinfo =
{
name = "Discord Test",
author = "Deathknife",
description = "",
version = PLUGIN_VERSION,
url = ""
};
DiscordBot gBot;
public void OnPluginStart() {
RegConsoleCmd("sm_getguilds", Cmd_GetGuilds);
RegConsoleCmd("sm_recreatebot", Cmd_RecreateBot);
RegConsoleCmd("sm_webhooktest", Cmd_Webhook);
RegConsoleCmd("sm_sendmsg", Cmd_SendMsg);
RegConsoleCmd("sm_getroles", Cmd_GetRoles);
}
public void OnAllPluginsLoaded() {
gBot = new DiscordBot(BOT_TOKEN);
}
public Action Cmd_Webhook(int client, int argc) {
DiscordWebHook hook = new DiscordWebHook(WEBHOOK);
hook.SlackMode = true;
hook.SetContent("@here");
hook.SetUsername("Server");
MessageEmbed Embed = new MessageEmbed();
Embed.SetColor("#ff2222");
Embed.SetTitle("Testing WebHook");
Embed.AddField("Field1", "Test1", true);
Embed.AddField("abc def", "deef", true);
hook.Embed(Embed);
hook.Send();
delete hook;
hook = new DiscordWebHook("");
hook.SetUsername("Testing");
hook.SlackMode = false;
hook.SetContent("Testing 1 2 3");
hook.Send();
delete hook;
}
public Action Cmd_GetRoles(int client, int argc) {
if(client == 0)
{
ReplyToCommand(client, "[SM] This command cannot be used from console.");
return Plugin_Handled;
}
gBot.GetGuilds(GuildListGetRoles, _, GetClientUserId(client));
ReplyToCommand(client, "Trying!");
return Plugin_Handled;
}
public void GuildListGetRoles(DiscordBot bot, char[] id, char[] name, char[] icon, bool owner, int permissions, any data) {
int client = GetClientOfUserId(data);
if(client > 0 && IsClientConnected(client) && IsClientInGame(client)) {
bot.GetGuildRoles(id, OnGetRoles, data);
}
}
public void OnGetRoles(DiscordBot bot, char[] guild, RoleList roles, any data) {
PrintToChatAll("%i a", data);
int client = GetClientOfUserId(data);
if(client > 0 && IsClientConnected(client) && IsClientInGame(client)) {
PrintToConsole(client, "Roles for guild %s", guild);
for(int i = 0; i < roles.Size; i++) {
Role role = roles.Get(i);
char id[64];
char name[64];
role.GetID(id, sizeof(id));
role.GetName(name, sizeof(name));
PrintToConsole(client, "Role %s %s", id, name);
}
}
}
public Action Cmd_GetGuilds(int client, int argc) {
if(client == 0)
{
ReplyToCommand(client, "[SM] This command cannot be used from console.");
return Plugin_Handled;
}
gBot.GetGuilds(GuildList, GuildListAll, GetClientUserId(client));
ReplyToCommand(client, "Trying!");
return Plugin_Handled;
}
public Action Cmd_RecreateBot(int client, int argc) {
if(gBot != null) {
gBot.StopListening();
delete gBot;
}
gBot = new DiscordBot(BOT_TOKEN);
ReplyToCommand(client, "Recreated");
}
public void GuildList(DiscordBot bot, char[] id, char[] name, char[] icon, bool owner, int permissions, any data) {
int client = GetClientOfUserId(data);
if(client > 0 && IsClientConnected(client) && IsClientInGame(client)) {
PrintToConsole(client, "Guild [%s] [%s] [%s] [%i] [%i]", id, name, icon, owner, permissions);
gBot.GetGuildChannels(id, ChannelList, INVALID_FUNCTION, data);
}
}
public void ChannelList(DiscordBot bot, char[] guild, DiscordChannel Channel, any data) {
int client = GetClientOfUserId(data);
if(client > 0 && IsClientConnected(client) && IsClientInGame(client)) {
char name[32];
char id[32];
Channel.GetID(id, sizeof(id));
Channel.GetName(name, sizeof(name));
PrintToConsole(client, "Channel for Guild(%s) - [%s] [%s]", guild, id, name);
if(Channel.IsText) {
//Send a message with all ways
//gBot.SendMessage(Channel, "Sending message with DiscordBot.SendMessage");
//gBot.SendMessageToChannelID(id, "Sending message with DiscordBot.SendMessageToChannelID");
//Channel.SendMessage(gBot, "Sending message with DiscordChannel.SendMessage");
gBot.StartListeningToChannel(Channel, OnMessage);
}
}
}
public void OnMessage(DiscordBot Bot, DiscordChannel Channel, DiscordMessage message) {
char sMessage[2048];
message.GetContent(sMessage, sizeof(sMessage));
char sAuthor[128];
message.GetAuthor().GetUsername(sAuthor, sizeof(sAuthor));
PrintToChatAll("[DISCORD] %s: %s", sAuthor, sMessage);
if(StrEqual(sMessage, "Ping", false)) {
gBot.SendMessage(Channel, "Pong!");
}
}
public void GuildListAll(DiscordBot bot, ArrayList Alid, ArrayList Alname, ArrayList Alicon, ArrayList Alowner, ArrayList Alpermissions, any data) {
int client = GetClientOfUserId(data);
if(client > 0 && IsClientConnected(client) && IsClientInGame(client)) {
char id[32];
char name[64];
char icon[128];
bool owner;
int permissions;
PrintToConsole(client, "Dumping Guilds from arraylist");
for(int i = 0; i < Alid.Length; i++) {
GetArrayString(Alid, i, id, sizeof(id));
GetArrayString(Alname, i, name, sizeof(name));
GetArrayString(Alicon, i, icon, sizeof(icon));
owner = GetArrayCell(Alowner, i);
permissions = GetArrayCell(Alpermissions, i);
PrintToConsole(client, "Guild: [%s] [%s] [%s] [%i] [%i]", id, name, icon, owner, permissions);
}
}
}
public Action Cmd_SendMsg(int client, int argc) {
if(client == 0)
{
ReplyToCommand(client, "[SM] This command cannot be used from console.");
return Plugin_Handled;
}
if(argc != 2)
{
ReplyToCommand(client, "[SM] Usage: sm_sendmsg <channelid> <message>.");
return Plugin_Handled;
}
char channelid[64];
GetCmdArg(1, channelid, sizeof(channelid));
char message[256];
GetCmdArg(2, message, sizeof(message));
gBot.SendMessageToChannelID(channelid, message, OnMessageSent, GetClientUserId(client));
return Plugin_Handled;
}
public void OnMessageSent(DiscordBot bot, char[] channel, DiscordMessage message, any data)
{
int client = GetClientOfUserId(data);
ReplyToCommand(client, "Message sent!");
}

View File

@@ -0,0 +1,14 @@
# discord_test.sp
On the command: `sm_getguilds` / `!getguilds` the plugin will list all guilds and channels in the client console. It will hook into every channel and print any messages in discord channel to the server console. If the message is `Ping` it will reply with `Pong`. `sm_recreatebot` will delete the bot and create it again(for testing purposes)
Not recommended to use this plugin on live server.
# discord_announcement.sp
The plugin will hook into any channels with the name of `server-announcement` and print any messages sent in that channel to all players on servers with the `[Announcement]` prefix.
Should be fine to have this on a live server.
# discord_calladmin.sp
The plugin finds all channels with the name of `call-admin` and stores them. When a client types `!calladmin` it will send a message to all those channels.
Should be fine to have this on a live server.

View File

@@ -0,0 +1,57 @@
#pragma semicolon 1
#define PLUGIN_VERSION "1.00"
#include <sourcemod>
#include <csgocolors>
#include <discord>
public Plugin myinfo = {
name = "Announcements from Discord",
author = "Deathknife",
description = "",
version = PLUGIN_VERSION,
url = ""
};
DiscordBot gBot = null;
public void OnPluginStart() {
//
}
public OnAllPluginsLoaded() {
//Create bot with a token
gBot = new DiscordBot("<Bot Token>");
//Check for messages every 5 seconds. Default is 1 second. Since this is announcement, time accuracy is not necessary
gBot.MessageCheckInterval = 5.0;
//Get all guilds then channels to find any channel with the name of server-announcement
gBot.GetGuilds(GuildList);
}
public void GuildList(DiscordBot bot, char[] id, char[] name, char[] icon, bool owner, int permissions, any data) {
//Retrieve all channels for the guild
bot.GetGuildChannels(id, ChannelList);
}
public void ChannelList(DiscordBot bot, char[] guild, DiscordChannel Channel, any data) {
//Verify that the channel is a text channel
if(Channel.IsText) {
//Get name of channel
char name[32];
Channel.GetName(name, sizeof(name));
//Compare name of channel to 'server-announcement'
if(StrEqual(name, "server-announcement", false)) {
//Start listening to channel
bot.StartListeningToChannel(Channel, OnMessage);
}
}
}
public void OnMessage(DiscordBot Bot, DiscordChannel Channel, const char[] message) {
//Received a message, print it out.
CPrintToChatAll("{green}[Announcement]{normal} %s", message);
}

View File

@@ -0,0 +1,119 @@
#pragma semicolon 1
#define PLUGIN_VERSION "1.00"
#include <sourcemod>
#include <csgocolors>
#include <discord>
#include <SteamWorks>
public Plugin myinfo = {
name = "Call Admin to Discord",
author = "Deathknife",
description = "",
version = PLUGIN_VERSION,
url = ""
};
DiscordBot gBot = null;
ArrayList CallAdminChannels = null;
ConVar gHostPort;
ConVar gHostname;
//To prevent spam
int LastUsage[MAXPLAYERS + 1];
public void OnPluginStart() {
RegConsoleCmd("sm_calladmin", Cmd_CallAdmin);
CallAdminChannels = new ArrayList();
gHostname = FindConVar("hostname");
gHostPort = FindConVar("hostport");
}
public OnAllPluginsLoaded() {
//Create bot with a token
gBot = new DiscordBot("<Bot Token>");
//Get all guilds then channels to find any channel with the name of call-admin
gBot.GetGuilds(GuildList);
}
public Action Cmd_CallAdmin(int client, int argc) {
//Add minimum 60 seconds interval before calling an admin again
if(GetTime() < LastUsage[client] + 60) {
CReplyToCommand(client, "{red}Please wait before calling an admin again!");
return Plugin_Continue;
}
//Format Message to send
char message[256];
char name[32];
GetClientName(client, name, sizeof(name));
//Replace ` with nothing as we will use `NAME` in discord message
ReplaceString(name, sizeof(name), "`", "");
char authid[32];
GetClientAuthId(client, AuthId_Steam2, authid, sizeof(authid));
char ip[64];
GetIP(ip, sizeof(ip));
char hostname[64];
GetConVarString(gHostname, hostname, sizeof(hostname));
FormatEx(message, sizeof(message), "`%s` (`%s`) has called an Admin on %s\nConnect: steam://connect/%s", name, authid, hostname, ip);
//Send Message to all channels we stored
for(int i = 0; i < CallAdminChannels.Length; i++) {
DiscordChannel Channel = CallAdminChannels.Get(i);
Channel.SendMessage(gBot, message);
}
CReplyToCommand(client, "{green}Called an Admin");
LastUsage[client] = GetTime();
return Plugin_Continue;
}
public void OnClientPutInServer(int client) {
LastUsage[client] = 0;
}
public void GuildList(DiscordBot bot, char[] id, char[] name, char[] icon, bool owner, int permissions, any data) {
//Retrieve all channels for the guild
//PrintToServer("Guild %s", name);
bot.GetGuildChannels(id, ChannelList);
}
public void ChannelList(DiscordBot bot, char[] guild, DiscordChannel Channel, any data) {
//Verify that the channel is a text channel
if(Channel.IsText) {
//Get name of channel
char name[32];
Channel.GetName(name, sizeof(name));
//PrintToServer("Channel name %s", name);
//Compare name of channel to 'call-admin'
if(StrEqual(name, "call-admin", false)) {
PrintToServer("Added Call Admin Channel");
//Store The Channel
//Duplicate the Channel handle as the 'Channel' handle is closed after the forwards are called
DiscordChannel newChannel = view_as<DiscordChannel>(CloneHandle(Channel));
//Store it into array
CallAdminChannels.Push(newChannel);
}
}
}
//Stores IP into buffer using SteamWorks
stock void GetIP(char[] buffer, int maxlength) {
int ip[4];
SteamWorks_GetPublicIP(ip);
strcopy(buffer, maxlength, "");
FormatEx(buffer, maxlength, "%d.%d.%d.%d:%d", ip[0], ip[1], ip[2], ip[3], gHostPort.IntValue);
}

View File

@@ -0,0 +1,135 @@
#pragma semicolon 1
#define PLUGIN_VERSION "1.10"
#include <sourcemod>
#include <discord>
#define BOT_TOKEN "INSERT TOKEN HERE"
public Plugin myinfo =
{
name = "Discord Test",
author = "Deathknife",
description = "",
version = PLUGIN_VERSION,
url = ""
};
DiscordBot gBot;
public void OnPluginStart() {
RegConsoleCmd("sm_getguilds", Cmd_GetGuilds);
RegConsoleCmd("sm_recreatebot", Cmd_RecreateBot);
RegConsoleCmd("sm_webhooktest", Cmd_Webhook);
}
public void OnPluginEnd() {
if(gBot != null) {
gBot.DestroyData();
delete gBot;
}
}
public void OnAllPluginsLoaded() {
gBot = new DiscordBot(BOT_TOKEN);
}
public Action Cmd_Webhook(int client, int argc) {
DiscordWebHook hook = new DiscordWebHook("https://ptb.discordapp.com/api/webhooks/265660968086929419/z3_F8CEGNu1Wtdygv4v0Pg4YRBA8QxgmzFKqjkEleSf2BOuQ8Xz7Ub05ku2j-O2vofy7");
hook.SlackMode = true;
hook.SetUsername("Server");
hook.SetColor("#ff2222");
hook.SetTitle("Testing WebHook");
hook.SetContent("@here");
hook.AddField("Field1", "Test1", true);
hook.AddField("abc def", "deef", true);
hook.Send();
delete hook;
hook = new DiscordWebHook("https://ptb.discordapp.com/api/webhooks/265660968086929419/z3_F8CEGNu1Wtdygv4v0Pg4YRBA8QxgmzFKqjkEleSf2BOuQ8Xz7Ub05ku2j-O2vofy7");
hook.SetUsername("Testing");
hook.SlackMode = false;
hook.SetContent("Testing 1 2 3");
hook.Send();
delete hook;
}
public Action Cmd_GetGuilds(int client, int argc) {
gBot.GetGuilds(GuildList, GuildListAll, GetClientUserId(client));
ReplyToCommand(client, "Trying!");
return Plugin_Handled;
}
public Action Cmd_RecreateBot(int client, int argc) {
if(gBot != null) {
gBot.DestroyData();
delete gBot;
}
gBot = new DiscordBot(BOT_TOKEN);
ReplyToCommand(client, "Recreated");
}
public void GuildList(DiscordBot bot, char[] id, char[] name, char[] icon, bool owner, int permissions, any data) {
int client = GetClientOfUserId(data);
if(client > 0 && IsClientConnected(client) && IsClientInGame(client)) {
PrintToConsole(client, "Guild [%s] [%s] [%s] [%i] [%i]", id, name, icon, owner, permissions);
gBot.GetGuildChannels(id, ChannelList, INVALID_FUNCTION, data);
}
}
public void ChannelList(DiscordBot bot, char[] guild, DiscordChannel Channel, any data) {
int client = GetClientOfUserId(data);
if(client > 0 && IsClientConnected(client) && IsClientInGame(client)) {
char name[32];
char id[32];
Channel.GetID(id, sizeof(id));
Channel.GetName(name, sizeof(name));
PrintToConsole(client, "Channel for Guild(%s) - [%s] [%s]", guild, id, name);
if(Channel.IsText) {
//Send a message with all ways
gBot.SendMessage(Channel, "Sending message with DiscordBot.SendMessage");
gBot.SendMessageToChannelID(id, "Sending message with DiscordBot.SendMessageToChannelID");
Channel.SendMessage(gBot, "Sending message with DiscordChannel.SendMessage");
gBot.StartListeningToChannel(Channel, OnMessage);
}
}
}
public void OnMessage(DiscordBot Bot, DiscordChannel Channel, const char[] message) {
PrintToServer("Message from discord: %s", message);
if(StrEqual(message, "Ping", false)) {
gBot.SendMessage(Channel, "Pong!");
}
}
public void GuildListAll(DiscordBot bot, ArrayList Alid, ArrayList Alname, ArrayList Alicon, ArrayList Alowner, ArrayList Alpermissions, any data) {
int client = GetClientOfUserId(data);
if(client > 0 && IsClientConnected(client) && IsClientInGame(client)) {
char id[32];
char name[64];
char icon[128];
bool owner;
int permissions;
PrintToConsole(client, "Dumping Guilds from arraylist");
for(int i = 0; i < Alid.Length; i++) {
GetArrayString(Alid, i, id, sizeof(id));
GetArrayString(Alname, i, name, sizeof(name));
GetArrayString(Alicon, i, icon, sizeof(icon));
owner = GetArrayCell(Alowner, i);
permissions = GetArrayCell(Alpermissions, i);
PrintToConsole(client, "Guild: [%s] [%s] [%s] [%i] [%i]", id, name, icon, owner, permissions);
}
}
}
public Action Cmd_SendMsg(int client, int argc) {
//
}

View File

@@ -0,0 +1,559 @@
/**************************************************************************
* *
* Colored Chat Functions *
* Author: exvel, Editor: Popoklopsi, Powerlord, Bara *
* Version: 1.1.3 *
* *
**************************************************************************/
#if defined _colors_included
#endinput
#endif
#define _colors_included
#define MAX_MESSAGE_LENGTH 250
#define MAX_COLORS 16
#define SERVER_INDEX 0
#define NO_INDEX -1
#define NO_PLAYER -2
enum Colors
{
Color_Default = 0,
Color_Darkred,
Color_Pink,
Color_Green,
Color_Lightgreen,
Color_Lime,
Color_Red,
Color_Grey,
Color_Olive,
Color_A,
Color_Lightblue,
Color_Blue,
Color_D,
Color_Purple,
Color_Darkrange,
Color_Orange
}
/* Colors' properties */
new String:CTag[][] = {"{normal}", "{darkred}", "{pink}", "{green}", "{lightgreen}", "{lime}", "{red}", "{grey}", "{olive}", "{a}", "{lightblue}", "{blue}", "{d}", "{purple}", "{darkorange}", "{orange}"};
new String:CTagCode[][] = {"\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\x09", "\x0A", "\x0B", "\x0C", "\x0D", "\x0E", "\x0F", "\x10"};
new bool:CTagReqSayText2[] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false};
new bool:CEventIsHooked = false;
new bool:CSkipList[MAXPLAYERS+1] = {false,...};
/* Game default profile */
new bool:CProfile_Colors[] = {true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false};
new CProfile_TeamIndex[] = {NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX};
new bool:CProfile_SayText2 = false;
/**
* Prints a message to a specific client in the chat area.
* Supports color tags.
*
* @param client Client index.
* @param szMessage Message (formatting rules).
* @return No return
*
* On error/Errors: If the client is not connected an error will be thrown.
*/
stock CPrintToChat(client, const String:szMessage[], any:...)
{
if (client <= 0 || client > MaxClients)
ThrowError("Invalid client index %d", client);
if (!IsClientInGame(client))
ThrowError("Client %d is not in game", client);
decl String:szBuffer[MAX_MESSAGE_LENGTH];
decl String:szCMessage[MAX_MESSAGE_LENGTH];
SetGlobalTransTarget(client);
Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage);
VFormat(szCMessage, sizeof(szCMessage), szBuffer, 3);
new index = CFormat(szCMessage, sizeof(szCMessage));
if (index == NO_INDEX)
PrintToChat(client, "%s", szCMessage);
else
CSayText2(client, index, szCMessage);
}
stock CReplyToCommand(client, const String:szMessage[], any:...)
{
decl String:szCMessage[MAX_MESSAGE_LENGTH];
VFormat(szCMessage, sizeof(szCMessage), szMessage, 3);
if (client == 0)
{
CRemoveTags(szCMessage, sizeof(szCMessage));
PrintToServer("%s", szCMessage);
}
else if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE)
{
CRemoveTags(szCMessage, sizeof(szCMessage));
PrintToConsole(client, "%s", szCMessage);
}
else
{
CPrintToChat(client, "%s", szCMessage);
}
}
/**
* Prints a message to all clients in the chat area.
* Supports color tags.
*
* @param client Client index.
* @param szMessage Message (formatting rules)
* @return No return
*/
stock CPrintToChatAll(const String:szMessage[], any:...)
{
decl String:szBuffer[MAX_MESSAGE_LENGTH];
for (new i = 1; i <= MaxClients; i++)
{
if (IsClientInGame(i) && !IsFakeClient(i) && !CSkipList[i])
{
SetGlobalTransTarget(i);
VFormat(szBuffer, sizeof(szBuffer), szMessage, 2);
CPrintToChat(i, "%s", szBuffer);
}
CSkipList[i] = false;
}
}
/**
* Prints a message to a specific client in the chat area.
* Supports color tags and teamcolor tag.
*
* @param client Client index.
* @param author Author index whose color will be used for teamcolor tag.
* @param szMessage Message (formatting rules).
* @return No return
*
* On error/Errors: If the client or author are not connected an error will be thrown.
*/
stock CPrintToChatEx(client, author, const String:szMessage[], any:...)
{
if (client <= 0 || client > MaxClients)
ThrowError("Invalid client index %d", client);
if (!IsClientInGame(client))
ThrowError("Client %d is not in game", client);
if (author < 0 || author > MaxClients)
ThrowError("Invalid client index %d", author);
decl String:szBuffer[MAX_MESSAGE_LENGTH];
decl String:szCMessage[MAX_MESSAGE_LENGTH];
SetGlobalTransTarget(client);
Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage);
VFormat(szCMessage, sizeof(szCMessage), szBuffer, 4);
new index = CFormat(szCMessage, sizeof(szCMessage), author);
if (index == NO_INDEX)
PrintToChat(client, "%s", szCMessage);
else
CSayText2(client, author, szCMessage);
}
/**
* Prints a message to all clients in the chat area.
* Supports color tags and teamcolor tag.
*
* @param author Author index whos color will be used for teamcolor tag.
* @param szMessage Message (formatting rules).
* @return No return
*
* On error/Errors: If the author is not connected an error will be thrown.
*/
stock CPrintToChatAllEx(author, const String:szMessage[], any:...)
{
if (author < 0 || author > MaxClients)
ThrowError("Invalid client index %d", author);
if (!IsClientInGame(author))
ThrowError("Client %d is not in game", author);
decl String:szBuffer[MAX_MESSAGE_LENGTH];
for (new i = 1; i <= MaxClients; i++)
{
if (IsClientInGame(i) && !IsFakeClient(i) && !CSkipList[i])
{
SetGlobalTransTarget(i);
VFormat(szBuffer, sizeof(szBuffer), szMessage, 3);
CPrintToChatEx(i, author, "%s", szBuffer);
}
CSkipList[i] = false;
}
}
/**
* Removes color tags from the string.
*
* @param szMessage String.
* @return No return
*/
stock CRemoveTags(String:szMessage[], maxlength)
{
for (new i = 0; i < MAX_COLORS; i++)
ReplaceString(szMessage, maxlength, CTag[i], "", false);
ReplaceString(szMessage, maxlength, "{teamcolor}", "", false);
}
/**
* Checks whether a color is allowed or not
*
* @param tag Color Tag.
* @return True when color is supported, otherwise false
*/
stock CColorAllowed(Colors:color)
{
if (!CEventIsHooked)
{
CSetupProfile();
CEventIsHooked = true;
}
return CProfile_Colors[color];
}
/**
* Replace the color with another color
* Handle with care!
*
* @param color color to replace.
* @param newColor color to replace with.
* @noreturn
*/
stock CReplaceColor(Colors:color, Colors:newColor)
{
if (!CEventIsHooked)
{
CSetupProfile();
CEventIsHooked = true;
}
CProfile_Colors[color] = CProfile_Colors[newColor];
CProfile_TeamIndex[color] = CProfile_TeamIndex[newColor];
CTagReqSayText2[color] = CTagReqSayText2[newColor];
Format(CTagCode[color], sizeof(CTagCode[]), CTagCode[newColor])
}
/**
* This function should only be used right in front of
* CPrintToChatAll or CPrintToChatAllEx and it tells
* to those funcions to skip specified client when printing
* message to all clients. After message is printed client will
* no more be skipped.
*
* @param client Client index
* @return No return
*/
stock CSkipNextClient(client)
{
if (client <= 0 || client > MaxClients)
ThrowError("Invalid client index %d", client);
CSkipList[client] = true;
}
/**
* Replaces color tags in a string with color codes
*
* @param szMessage String.
* @param maxlength Maximum length of the string buffer.
* @return Client index that can be used for SayText2 author index
*
* On error/Errors: If there is more then one team color is used an error will be thrown.
*/
stock CFormat(String:szMessage[], maxlength, author=NO_INDEX)
{
decl String:szGameName[30];
GetGameFolderName(szGameName, sizeof(szGameName));
/* Hook event for auto profile setup on map start */
if (!CEventIsHooked)
{
CSetupProfile();
HookEvent("server_spawn", CEvent_MapStart, EventHookMode_PostNoCopy);
CEventIsHooked = true;
}
new iRandomPlayer = NO_INDEX;
// On CS:GO set invisible precolor
if (StrEqual(szGameName, "csgo", false))
Format(szMessage, maxlength, " \x01\x0B\x01%s", szMessage);
/* If author was specified replace {teamcolor} tag */
if (author != NO_INDEX)
{
if (CProfile_SayText2)
{
ReplaceString(szMessage, maxlength, "{teamcolor}", "\x03", false);
iRandomPlayer = author;
}
/* If saytext2 is not supported by game replace {teamcolor} with green tag */
else
ReplaceString(szMessage, maxlength, "{teamcolor}", CTagCode[Color_Green], false);
}
else
ReplaceString(szMessage, maxlength, "{teamcolor}", "", false);
/* For other color tags we need a loop */
for (new i = 0; i < MAX_COLORS; i++)
{
/* If tag not found - skip */
if (StrContains(szMessage, CTag[i], false) == -1)
continue;
/* If tag is not supported by game replace it with green tag */
else if (!CProfile_Colors[i])
ReplaceString(szMessage, maxlength, CTag[i], CTagCode[Color_Green], false);
/* If tag doesn't need saytext2 simply replace */
else if (!CTagReqSayText2[i])
ReplaceString(szMessage, maxlength, CTag[i], CTagCode[i], false);
/* Tag needs saytext2 */
else
{
/* If saytext2 is not supported by game replace tag with green tag */
if (!CProfile_SayText2)
ReplaceString(szMessage, maxlength, CTag[i], CTagCode[Color_Green], false);
/* Game supports saytext2 */
else
{
/* If random player for tag wasn't specified replace tag and find player */
if (iRandomPlayer == NO_INDEX)
{
/* Searching for valid client for tag */
iRandomPlayer = CFindRandomPlayerByTeam(CProfile_TeamIndex[i]);
/* If player not found replace tag with green color tag */
if (iRandomPlayer == NO_PLAYER)
ReplaceString(szMessage, maxlength, CTag[i], CTagCode[Color_Green], false);
/* If player was found simply replace */
else
ReplaceString(szMessage, maxlength, CTag[i], CTagCode[i], false);
}
/* If found another team color tag throw error */
else
{
//ReplaceString(szMessage, maxlength, CTag[i], "");
ThrowError("Using two team colors in one message is not allowed");
}
}
}
}
return iRandomPlayer;
}
/**
* Founds a random player with specified team
*
* @param color_team Client team.
* @return Client index or NO_PLAYER if no player found
*/
stock CFindRandomPlayerByTeam(color_team)
{
if (color_team == SERVER_INDEX)
return 0;
else
{
for (new i = 1; i <= MaxClients; i++)
{
if (IsClientInGame(i) && GetClientTeam(i) == color_team)
return i;
}
}
return NO_PLAYER;
}
/**
* Sends a SayText2 usermessage to a client
*
* @param szMessage Client index
* @param maxlength Author index
* @param szMessage Message
* @return No return.
*/
stock CSayText2(client, author, const String:szMessage[])
{
new Handle:hBuffer = StartMessageOne("SayText2", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS);
if(GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf)
{
PbSetInt(hBuffer, "ent_idx", author);
PbSetBool(hBuffer, "chat", true);
PbSetString(hBuffer, "msg_name", szMessage);
PbAddString(hBuffer, "params", "");
PbAddString(hBuffer, "params", "");
PbAddString(hBuffer, "params", "");
PbAddString(hBuffer, "params", "");
}
else
{
BfWriteByte(hBuffer, author);
BfWriteByte(hBuffer, true);
BfWriteString(hBuffer, szMessage);
}
EndMessage();
}
/**
* Creates game color profile
* This function must be edited if you want to add more games support
*
* @return No return.
*/
stock CSetupProfile()
{
decl String:szGameName[30];
GetGameFolderName(szGameName, sizeof(szGameName));
if (StrEqual(szGameName, "cstrike", false))
{
CProfile_Colors[Color_Lightgreen] = true;
CProfile_Colors[Color_Red] = true;
CProfile_Colors[Color_Blue] = true;
CProfile_Colors[Color_Olive] = true;
CProfile_TeamIndex[Color_Lightgreen] = SERVER_INDEX;
CProfile_TeamIndex[Color_Red] = 2;
CProfile_TeamIndex[Color_Blue] = 3;
CProfile_SayText2 = true;
}
else if (StrEqual(szGameName, "csgo", false))
{
CProfile_Colors[Color_Default] = true;
CProfile_Colors[Color_Darkred] = true;
CProfile_Colors[Color_Pink] = true;
CProfile_Colors[Color_Green] = true;
CProfile_Colors[Color_Lightgreen] = true;
CProfile_Colors[Color_Lime] = true;
CProfile_Colors[Color_Red] = true;
CProfile_Colors[Color_Grey] = true;
CProfile_Colors[Color_Olive] = true;
CProfile_Colors[Color_A] = true;
CProfile_Colors[Color_Lightblue] = true;
CProfile_Colors[Color_Blue] = true;
CProfile_Colors[Color_D] = true;
CProfile_Colors[Color_Purple] = true;
CProfile_Colors[Color_Darkrange] = true;
CProfile_Colors[Color_Orange] = true;
CProfile_Colors[Color_Red] = true;
CProfile_Colors[Color_Blue] = true;
CProfile_Colors[Color_Olive] = true;
CProfile_Colors[Color_Darkred] = true;
CProfile_Colors[Color_Lime] = true;
CProfile_Colors[Color_Purple] = true;
CProfile_Colors[Color_Grey] = true;
CProfile_Colors[Color_Orange] = true;
CProfile_TeamIndex[Color_Red] = 2;
CProfile_TeamIndex[Color_Blue] = 3;
CProfile_SayText2 = true;
}
else if (StrEqual(szGameName, "tf", false))
{
CProfile_Colors[Color_Lightgreen] = true;
CProfile_Colors[Color_Red] = true;
CProfile_Colors[Color_Blue] = true;
CProfile_Colors[Color_Olive] = true;
CProfile_TeamIndex[Color_Lightgreen] = SERVER_INDEX;
CProfile_TeamIndex[Color_Red] = 2;
CProfile_TeamIndex[Color_Blue] = 3;
CProfile_SayText2 = true;
}
else if (StrEqual(szGameName, "left4dead", false) || StrEqual(szGameName, "left4dead2", false))
{
CProfile_Colors[Color_Lightgreen] = true;
CProfile_Colors[Color_Red] = true;
CProfile_Colors[Color_Blue] = true;
CProfile_Colors[Color_Olive] = true;
CProfile_TeamIndex[Color_Lightgreen] = SERVER_INDEX;
CProfile_TeamIndex[Color_Red] = 3;
CProfile_TeamIndex[Color_Blue] = 2;
CProfile_SayText2 = true;
}
else if (StrEqual(szGameName, "hl2mp", false))
{
/* hl2mp profile is based on mp_teamplay convar */
if (GetConVarBool(FindConVar("mp_teamplay")))
{
CProfile_Colors[Color_Red] = true;
CProfile_Colors[Color_Blue] = true;
CProfile_Colors[Color_Olive] = true;
CProfile_TeamIndex[Color_Red] = 3;
CProfile_TeamIndex[Color_Blue] = 2;
CProfile_SayText2 = true;
}
else
{
CProfile_SayText2 = false;
CProfile_Colors[Color_Olive] = true;
}
}
else if (StrEqual(szGameName, "dod", false))
{
CProfile_Colors[Color_Olive] = true;
CProfile_SayText2 = false;
}
/* Profile for other games */
else
{
if (GetUserMessageId("SayText2") == INVALID_MESSAGE_ID)
{
CProfile_SayText2 = false;
}
else
{
CProfile_Colors[Color_Red] = true;
CProfile_Colors[Color_Blue] = true;
CProfile_TeamIndex[Color_Red] = 2;
CProfile_TeamIndex[Color_Blue] = 3;
CProfile_SayText2 = true;
}
}
}
public Action:CEvent_MapStart(Handle:event, const String:name[], bool:dontBroadcast)
{
CSetupProfile();
for (new i = 1; i <= MaxClients; i++)
CSkipList[i] = false;
}

View File

@@ -0,0 +1,141 @@
#if defined _discord_included
#endinput
#endif
#define _discord_included
#include <smjansson>
#include <discord/stocks>
typedef DiscordGuildsRetrieve = function void (DiscordBot bot, char[] id, char[] name, char[] icon, bool owner, int permissions, any data);
typedef DiscordGuildsRetrievedAll = function void (DiscordBot bot, ArrayList id, ArrayList name, ArrayList icon, ArrayList owner, ArrayList permissions, any data);
//Channel are Handles that are closed immediately after forwards called. To keep, clone. Or store id if thats what you want
typedef DiscordGuildChannelsRetrieve = function void (DiscordBot bot, char[] guild, DiscordChannel Channel, any data);
typedef DiscordGuildChannelsRetrieveAll = function void (DiscordBot bot, char[] guild, ArrayList Channels, any data);
typedef DiscordGuildGetRoles = function void (DiscordBot bot, char[] guild, RoleList Roles, any data);
/**
* Called when message is received
* bot/channel/message are all destroyed after callback is sent.
* You can clone it if need to keep.
*/
typeset OnChannelMessage {
function void(DiscordBot bot, DiscordChannel channel, DiscordMessage message);
};
typedef OnGetReactions = function void (DiscordBot bot, ArrayList Users, char[] channelID, const char[] messageID, const char[] emoji, any data);
typedef OnMessageSent = function void(DiscordBot bot, char[] channel, DiscordMessage message, any data);
typedef OnMessageDeleted = function void(DiscordBot bot, any data);
//hMemberList is JSON array containing guild members
typedef OnGetMembers = function void(DiscordBot bot, char[] guild, Handle hMemberList);
methodmap Role < Handle {
public void GetID(char[] buffer, int maxlength) {
JsonObjectGetString(this, "id", buffer, maxlength);
}
public void GetName(char[] buffer, int maxlength) {
JsonObjectGetString(this, "name", buffer, maxlength);
}
public int GetColor() {
return JsonObjectGetInt(this, "color");
}
public int GetPosition() {
return JsonObjectGetInt(this, "position");
}
public int GetPermissions() {
return JsonObjectGetInt(this, "permissions");
}
public bool Hoist() {
return JsonObjectGetBool(this, "hoist");
}
public bool Managed() {
return JsonObjectGetBool(this, "managed");
}
public bool Mentionable() {
return JsonObjectGetBool(this, "mentionable");
}
};
methodmap RoleList < Handle {
property int Size {
public get() {
return json_array_size(this);
}
}
public Role GetRole(int i) {
return view_as<Role>(
json_array_get(this, i)
);
}
public Role Get(int i) {
return this.GetRole(i);
}
};
/*
{
"id": "80351110224678912",
"username": "Nelly",
"discriminator": "1337",
"avatar": "8342729096ea3675442027381ff50dfe",
"verified": true,
"email": "nelly@discordapp.com"
}
*/
//It's a JSON Handle with the above info TODO stop using natives!
methodmap DiscordUser < Handle {
public native void GetID(char[] buffer, int maxlength);
public native void GetUsername(char[] buffer, int maxlength);
public native void GetDiscriminator(char[] buffer, int maxlength);
public int GetDiscriminatorInt() {
char buffer[16];
this.GetDiscriminator(buffer, sizeof(buffer));
return StringToInt(buffer);
}
public native void GetAvatar(char[] buffer, int maxlength);
public native bool IsVerified();
public native void GetEmail(char[] buffer, int maxlength);
public native bool IsBot();
};
/*
{"timestamp": "2017-01-15T20:26:35.353000+00:00", "mention_everyone": false, "id": "270287641155469313", "pinned": false, "edited_timestamp": null, "author": {"username": "DK-Bot", "discriminator": "6274", "bot": true, "id": "186256454863290369", "avatar": null}, "mention_roles": [], "content": "ab", "channel_id": "229677130483499008", "mentions": [], "type": 0}
*/
methodmap DiscordMessage < Handle {
public native void GetID(char[] buffer, int maxlength);
public native bool IsPinned();
public native DiscordUser GetAuthor();
public native void GetContent(char[] buffer, int maxlength);
public native void GetChannelID(char[] buffer, int maxlength);
};
#include <discord/channel>
#include <discord/message_embed>
#include <discord/webhook>
#include <discord/bot>
#include <discord/GuildMember>

View File

@@ -0,0 +1,29 @@
methodmap DiscordGuildUser < Handle {
//Returns User Object
public DiscordUser GetUser() {
return view_as<DiscordUser>(json_object_get(this, "user"));
}
//Returns player's nick
public void GetNickname(char[] buffer, int maxlength) {
JsonObjectGetString(this, "nick", buffer, maxlength);
}
//Returns JSON array list of roles. You can manually loop through them for now.
public Handle GetRoles() {
return json_object_get(this, "roles");
}
//Returns the date the user joined the guild in format: "2015-04-26T06:26:56.936000+00:00"
public void GetJoinedAt(char[] buffer, int maxlength) {
JsonObjectGetString(this, "joined_at", buffer, maxlength);
}
public bool IsDeaf() {
return JsonObjectGetBool(this, "deaf");
}
public bool IsMute() {
return JsonObjectGetBool(this, "mute");
}
};

View File

@@ -0,0 +1,231 @@
methodmap DiscordBot < StringMap {
public DiscordBot(const char[] token) {
Handle json = json_object();
json_object_set_new(json, "token", json_string(token));
return view_as<DiscordBot>(json);
}
public void StopListening() {
json_object_del(this, "listeningChannels");
}
property float MessageCheckInterval {
public get() {
return JsonObjectGetFloat(this, "messageInterval", 3.0);
}
public set(float value) {
json_object_set_new(this, "messageInterval", json_real(value));
}
}
public native void StartTimer(DiscordChannel Channel, OnChannelMessage fCallback);
/**
* Retrieves a list of Channels the bot is listening to for messages
*/
public Handle GetListeningChannels() {
return json_object_get(this, "listeningChannels");
}
/**
* Checks if the bot is listening to channel for messages
* @param DiscordChannel Channel
*/
public bool IsListeningToChannel(DiscordChannel Channel) {
char id[32];
Channel.GetID(id, sizeof(id));
Handle hChannels = this.GetListeningChannels();
if(hChannels == null) return false;
for(int i = 0; i < json_array_size(hChannels); i++) {
DiscordChannel tempChannel = view_as<DiscordChannel>(json_array_get(hChannels, i));
static char tempID[32];
tempChannel.GetID(tempID, sizeof(tempID));
if(StrEqual(id, tempID, false)) {
delete tempChannel;
delete hChannels;
return true;
}
delete tempChannel;
}
delete hChannels;
return false;
}
/**
* Checks if the bot is listening to channel for messages
* @param DiscordChannel Channel
*/
public bool IsListeningToChannelID(const char[] id) {
Handle hChannels = this.GetListeningChannels();
if(hChannels == null) return false;
for(int i = 0; i < json_array_size(hChannels); i++) {
DiscordChannel tempChannel = view_as<DiscordChannel>(json_array_get(hChannels, i));
static char tempID[32];
tempChannel.GetID(tempID, sizeof(tempID));
if(StrEqual(id, tempID, false)) {
delete tempChannel;
delete hChannels;
return true;
}
delete tempChannel;
}
delete hChannels;
return false;
}
/**
* Stops the bot from listening to that channel for messages
* @param DiscordChannel Channel
*/
public void StopListeningToChannel(DiscordChannel Channel) {
char id[32];
Channel.GetID(id, sizeof(id));
Handle channels = this.GetListeningChannels();
if(channels == null) return;
for(int i = 0; i < json_array_size(channels); i++) {
DiscordChannel tempChannel = view_as<DiscordChannel>(json_array_get(channels, i));
static char tempID[32];
tempChannel.GetID(tempID, sizeof(tempID));
if(StrEqual(id, tempID, false)) {
json_array_remove(channels, i);
i--;
delete tempChannel;
}
}
delete channels;
}
/**
* Stops the bot from listening to that channel id for messages
* @param DiscordChannel Channel
*/
public void StopListeningToChannelID(const char[] id) {
Handle channels = this.GetListeningChannels();
if(channels == null) return;
for(int i = 0; i < json_array_size(channels); i++) {
DiscordChannel tempChannel = view_as<DiscordChannel>(json_array_get(channels, i));
static char tempID[32];
tempChannel.GetID(tempID, sizeof(tempID));
if(StrEqual(id, tempID, false)) {
json_array_remove(channels, i);
i--;
delete tempChannel;
}
}
delete channels;
}
public DiscordChannel GetListeningChannelByID(const char[] id) {
Handle channels = this.GetListeningChannels();
if(channels == null) return null;
for(int i = 0; i < json_array_size(channels); i++) {
DiscordChannel tempChannel = view_as<DiscordChannel>(json_array_get(channels, i));
static char tempID[32];
tempChannel.GetID(tempID, sizeof(tempID));
if(StrEqual(id, tempID, false)) {
delete channels;
return tempChannel;
}
}
delete channels;
return null;
}
/**
* Start listening to the channel for messages.
* The Channel handle is duplicated. Feel free to close yours.
* @param DiscordChannel Channel
*/
public void StartListeningToChannel(DiscordChannel Channel, OnChannelMessage fCallback) {
if(this.IsListeningToChannel(Channel)) return;
Handle channels = this.GetListeningChannels();
if(channels == null) {
channels = json_array();
json_object_set(this, "listeningChannels", channels);
}
json_array_append(channels, Channel);
//Handle fForward = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_String, Param_String, Param_String, Param_String, Param_String, Param_Cell);
//AddToForward(fForward, GetMyHandle(), callback);
this.StartTimer(Channel, fCallback);
}
public native void AddReactionID(const char[] channel, const char[] messageid, const char[] emoji);
public void AddReaction(DiscordChannel channel, const char[] messageid, const char[] emoji) {
char channelid[64];
channel.GetID(channelid, sizeof(channelid));
this.AddReactionID(channelid, messageid, emoji);
}
public native void DeleteReactionID(const char[] channel, const char[] messageid, const char[] emoji, const char[] user);
public void DeleteReaction(DiscordChannel channel, const char[] messageid, const char[] emoji, const char[] user) {
char chid[64];
channel.GetID(chid, sizeof(chid));
this.DeleteReactionID(chid, messageid, emoji, user);
}
public void DeleteReactionSelf(DiscordChannel channel, const char[] messageid, const char[] emoji) {
this.DeleteReaction(channel, messageid, emoji, "@me");
}
public void DeleteReactionAll(DiscordChannel channel, const char[] messageid, const char[] emoji) {
this.DeleteReaction(channel, messageid, emoji, "@all");
}
public void DeleteReactionSelfID(const char[] channel, const char[] messageid, const char[] emoji) {
this.DeleteReactionID(channel, messageid, emoji, "@me");
}
public void DeleteReactionAllID(const char[] channel, const char[] messageid, const char[] emoji) {
this.DeleteReactionID(channel, messageid, emoji, "@all");
}
public native void GetReactionID(const char[] channel, const char[] messageid, const char[] emoji, OnGetReactions fCallback=INVALID_FUNCTION, any data=0);
public void GetReaction(DiscordChannel channel, const char[] messageid, const char[] emoji, OnGetReactions fCallback=INVALID_FUNCTION, any data=0) {
char id[64];
channel.GetID(id, sizeof(id));
this.GetReactionID(id, messageid, emoji, fCallback, data);
}
public native void GetToken(char[] token, int maxlength);
public native void SendMessage(DiscordChannel channel, char[] message, OnMessageSent fCallback=INVALID_FUNCTION, any data=0);
public native void SendMessageToChannelID(char[] channel, char[] message, OnMessageSent fCallback=INVALID_FUNCTION, any data=0);
public native void DeleteMessageID(char[] channel, char[] message, OnMessageDeleted fCallback=INVALID_FUNCTION, any data=0);
public native void DeleteMessage(DiscordChannel channel, DiscordMessage message, OnMessageDeleted fCallback=INVALID_FUNCTION, any data=0);
public native void GetGuilds(DiscordGuildsRetrieve fCallback = INVALID_FUNCTION, DiscordGuildsRetrievedAll fCallbackAll = INVALID_FUNCTION, any data=0);
public native void GetGuildChannels(char[] guild, DiscordGuildChannelsRetrieve fCallback = INVALID_FUNCTION, DiscordGuildChannelsRetrieveAll fCallbackAll = INVALID_FUNCTION, any data=0);
/**
* ATM takes guild id, hopefully later on i will implement guild objects.
* Limit is from 1-1000
*/
public native void GetGuildMembers(char[] guild, OnGetMembers fCallback, int limit=250, char[] afterUserID="");
/**
* Same as above but displays ALL members, paginating automatically.
* perPage is how many it should display per callback. 1-1000
*/
public native void GetGuildMembersAll(char[] guild, OnGetMembers fCallback, int perPage=250, char[] afterUserID="");
public native void GetGuildRoles(char[] guild, DiscordGuildGetRoles fCallback, any data);
};

View File

@@ -0,0 +1,77 @@
enum
{
GUILD_TEXT = 0,
DM,
GUILD_VOICE,
GROUP_DM,
GUILD_CATEGORY
};
methodmap DiscordChannel < StringMap {
public DiscordChannel() {
Handle hObj = json_object();
return view_as<DiscordChannel>(hObj);
}
public native void SendMessage(DiscordBot Bot, char[] message, OnMessageSent fCallback=INVALID_FUNCTION, any data=0);
public void GetGuildID(char[] buffer, int maxlength) {
JsonObjectGetString(this, "guild_id", buffer, maxlength);
}
public void GetID(char[] buffer, int maxlength) {
JsonObjectGetString(this, "id", buffer, maxlength);
}
public void GetName(char[] buffer, int maxlength) {
JsonObjectGetString(this, "name", buffer, maxlength);
}
property int Position {
public get() {
return JsonObjectGetInt(this, "position");
}
}
property bool IsPrivate {
public get() {
return JsonObjectGetBool(this, "is_private");
}
}
public void GetTopic(char[] buffer, int maxlength) {
JsonObjectGetString(this, "topic", buffer, maxlength);
}
public void GetLastMessageID(char[] buffer, int maxlength) {
JsonObjectGetString(this, "last_message_id", buffer, maxlength);
}
public void SetLastMessageID(const char[] id) {
json_object_set_new(this, "last_message_id", json_string(id));
}
property int Type {
public get() {
return JsonObjectGetInt(this, "type");
}
}
property int Bitrate {
public get() {
return JsonObjectGetInt(this, "bitrate");
}
}
property int UserLimit {
public get() {
return JsonObjectGetInt(this, "user_limit");
}
}
property bool IsText {
public get() {
return this.Type == GUILD_TEXT;
}
}
};

View File

@@ -0,0 +1,128 @@
methodmap MessageEmbed < Handle {
public MessageEmbed() {
Handle hObj = json_object();
return view_as<MessageEmbed>(hObj);
}
public bool GetColor(char[] buffer, int maxlength) {
return JsonObjectGetString(this, "color", buffer, maxlength);
}
public void SetColor(const char[] color) {
json_object_set_new(this, "color", json_string(color));
}
public bool GetTitle(char[] buffer, int maxlength) {
return JsonObjectGetString(this, "title", buffer, maxlength);
}
public void SetTitle(const char[] title) {
json_object_set_new(this, "title", json_string(title));
}
public bool GetTitleLink(char[] buffer, int maxlength) {
return JsonObjectGetString(this, "title_link", buffer, maxlength);
}
public void SetTitleLink(const char[] title_link) {
json_object_set_new(this, "title_link", json_string(title_link));
}
public bool GetImage(char[] buffer, int maxlength) {
return JsonObjectGetString(this, "image_url", buffer, maxlength);
}
public void SetImage(const char[] image_url) {
json_object_set_new(this, "image_url", json_string(image_url));
}
public bool GetAuthor(char[] buffer, int maxlength) {
return JsonObjectGetString(this, "author_name", buffer, maxlength);
}
public void SetAuthor(const char[] author_name) {
json_object_set_new(this, "author_name", json_string(author_name));
}
public bool GetAuthorLink(char[] buffer, int maxlength) {
return JsonObjectGetString(this, "author_link", buffer, maxlength);
}
public void SetAuthorLink(const char[] author_link) {
json_object_set_new(this, "author_link", json_string(author_link));
}
public bool GetAuthorIcon(char[] buffer, int maxlength) {
return JsonObjectGetString(this, "author_icon", buffer, maxlength);
}
public void SetAuthorIcon(const char[] author_icon) {
json_object_set_new(this, "author_icon", json_string(author_icon));
}
public bool GetThumb(char[] buffer, int maxlength) {
return JsonObjectGetString(this, "thumb_url", buffer, maxlength);
}
public void SetThumb(const char[] thumb_url) {
json_object_set_new(this, "thumb_url", json_string(thumb_url));
}
public bool GetFooter(char[] buffer, int maxlength) {
return JsonObjectGetString(this, "footer", buffer, maxlength);
}
public void SetFooter(const char[] footer) {
json_object_set_new(this, "footer", json_string(footer));
}
public bool GetFooterIcon(char[] buffer, int maxlength) {
return JsonObjectGetString(this, "footer_icon", buffer, maxlength);
}
public void SetFooterIcon(const char[] footer_icon) {
json_object_set_new(this, "footer_icon", json_string(footer_icon));
}
/**
* Note: Setting Fields will delete the handle!
*/
property Handle Fields {
public get() {
return json_object_get(this, "fields");
}
public set(Handle value) {
json_object_set_new(this, "fields", value);
}
}
public void AddField(const char[] name, const char[] value, bool inline) {
Handle hObj = json_object();
json_object_set_new(hObj, "name", json_string(name));
json_object_set_new(hObj, "value", json_string(value));
json_object_set_new(hObj, "inline", json_boolean(inline));
Handle hArray = this.Fields;
if(this.Fields == null) {
hArray = json_array();
}
json_array_append_new(hArray, hObj);
this.Fields = hArray;
}
//Below don't support Slack Mode
public bool GetDescription(char[] buffer, int maxlength) {
return JsonObjectGetString(this, "description", buffer, maxlength);
}
public void SetDescription(const char[] description) {
json_object_set_new(this, "description", json_string(description));
}
public bool GetURL(char[] buffer, int maxlength) {
return JsonObjectGetString(this, "url", buffer, maxlength);
}
public void SetURL(const char[] url) {
json_object_set_new(this, "url", json_string(url));
}
};

View File

@@ -0,0 +1,82 @@
stock int JsonObjectGetInt(Handle hElement, char[] key) {
Handle hObject = json_object_get(hElement, key);
if(hObject == INVALID_HANDLE) return 0;
int value;
if(json_is_integer(hObject)) {
value = json_integer_value(hObject);
}else if(json_is_string(hObject)) {
char buffer[12];
json_string_value(hObject, buffer, sizeof(buffer));
value = StringToInt(buffer);
}
CloseHandle(hObject);
return value;
}
stock bool JsonObjectGetString(Handle hElement, char[] key, char[] buffer, maxlength) {
Handle hObject = json_object_get(hElement, key);
if(hObject == INVALID_HANDLE) return false;
if(json_is_integer(hObject)) {
IntToString(json_integer_value(hObject), buffer, maxlength);
}else if(json_is_string(hObject)) {
json_string_value(hObject, buffer, maxlength);
}else if(json_is_real(hObject)) {
FloatToString(json_real_value(hObject), buffer, maxlength);
}else if(json_is_true(hObject)) {
FormatEx(buffer, maxlength, "true");
}else if(json_is_false(hObject)) {
FormatEx(buffer, maxlength, "false");
}
CloseHandle(hObject);
return true;
}
stock bool JsonObjectGetBool(Handle hElement, char[] key, bool defaultvalue=false) {
Handle hObject = json_object_get(hElement, key);
if(hObject == INVALID_HANDLE) return defaultvalue;
bool ObjectBool = defaultvalue;
if(json_is_integer(hObject)) {
ObjectBool = view_as<bool>(json_integer_value(hObject));
}else if(json_is_string(hObject)) {
char buffer[11];
json_string_value(hObject, buffer, sizeof(buffer));
if(StrEqual(buffer, "true", false)) {
ObjectBool = true;
}else if(StrEqual(buffer, "false", false)) {
ObjectBool = false;
}else {
int x = StringToInt(buffer);
ObjectBool = view_as<bool>(x);
}
}else if(json_is_real(hObject)) {
ObjectBool = view_as<bool>(RoundToFloor(json_real_value(hObject)));
}else if(json_is_true(hObject)) {
ObjectBool = true;
}else if(json_is_false(hObject)) {
ObjectBool = false;
}
CloseHandle(hObject);
return ObjectBool;
}
stock float JsonObjectGetFloat(Handle hJson, char[] key, float defaultValue=0.0) {
Handle hObject = json_object_get(hJson, key);
if(hObject == INVALID_HANDLE) return defaultValue;
float value = defaultValue;
if(json_is_integer(hObject)) {
value = float(json_integer_value(hObject));
}else if(json_is_real(hObject)) {
value = json_real_value(hObject);
}else if(json_is_string(hObject)) {
char buffer[12];
json_string_value(hObject, buffer, sizeof(buffer));
value = StringToFloat(buffer);
}
CloseHandle(hObject);
return value;
}

View File

@@ -0,0 +1,142 @@
methodmap DiscordWebHook < Handle {
public DiscordWebHook(char[] url) {
Handle mp = json_object();
json_object_set_new(mp, "__url", json_string(url));
Handle data = json_object();
json_object_set_new(mp, "__data", data);
return view_as<DiscordWebHook>(mp);
}
public void GetUrl(char[] buffer, int maxlength) {
JsonObjectGetString(this, "__url", buffer, maxlength);
}
/**
* Gets/Sets if the hook should be sent as Slack.
* Note: color is different for slack than discord msg.
*
* @return True if Slack, otherwise false.
*/
property bool SlackMode {
public get() {
return JsonObjectGetBool(this, "__slack", false);
}
public set(bool value) {
json_object_set_new(this, "__slack", (value) ? json_true() : json_false());
}
}
property Handle Data {
public get() {
return json_object_get(this, "__data");
}
public set(Handle value) {
json_object_set_new(this, "__data", value);
}
}
public void UpdateDataObject(char[] key, Handle hObject) {
Handle data = this.Data;
json_object_set_new(data, key, hObject);
delete data;
}
public bool GetDataBool(char[] key, bool defaultValue=false) {
Handle data = this.Data;
bool value = JsonObjectGetBool(data, key, defaultValue);
delete data;
return value;
}
public bool GetDataString(char[] key, char[] buffer, int maxlength) {
Handle data = this.Data;
bool success = JsonObjectGetString(data, key, buffer, maxlength);
delete data;
return success;
}
/**
* Note: Deletes the MessageEmbed Object!
*/
public void Embed(MessageEmbed Object) {
//this.UpdateDataObject("embeds", Object);
Handle data = this.Data;
Handle hArray = json_object_get(data, "embeds");
if(hArray == null) {
hArray = json_array();
json_object_set(data, "embeds", hArray);
}
json_array_append_new(hArray, Object);
delete hArray;
delete data;
}
property bool tts {
public get() {
return this.GetDataBool("tts", false);
}
public set(bool value) {
this.UpdateDataObject("tts", json_boolean(value));
}
}
public bool GetUsername(char[] buffer, int maxlength) {
return this.GetDataString("username", buffer, maxlength);
}
public void SetUsername(const char[] name) {
this.UpdateDataObject("username", json_string(name));
}
public bool GetAvatar(char[] buffer, int maxlength) {
return this.GetDataString("icon_url", buffer, maxlength);
}
public void SetAvatar(const char[] icon_url) {
this.UpdateDataObject("icon_url", json_string(icon_url));
}
public bool GetContent(char[] buffer, int maxlength) {
return this.GetDataString("content", buffer, maxlength);
}
public void SetContent(const char[] content) {
this.UpdateDataObject("content", json_string(content));
}
/*property Handle OnComplete {
public get() {
Handle fForward = null;
if(!GetTrieValue(this, "callback", fForward)) {
return null;
}
return fForward;
}
public set(Handle value) {
SetTrieValue(this, "callback", value);
SetTrieValue(this, "plugin", GetMyHandle());
}
}
property Handle CallbackPlugin {
public get() {
Handle value = null;
if(!GetTrieValue(this, "plugin", value)) {
return null;
}
return value;
}
}*/
public native void Send();
};