diff --git a/GMDiscord.yyp b/GMDiscord.yyp index 628f974..ac1a5ba 100644 --- a/GMDiscord.yyp +++ b/GMDiscord.yyp @@ -3,20 +3,21 @@ "resourceVersion": "1.6", "name": "GMDiscord", "resources": [ + {"id":{"name":"scr_discordMessageComponents","path":"scripts/scr_discordMessageComponents/scr_discordMessageComponents.yy",},"order":7,}, + {"id":{"name":"scr_discordBot","path":"scripts/scr_discordBot/scr_discordBot.yy",},"order":3,}, {"id":{"name":"libfilesystem","path":"extensions/libfilesystem/libfilesystem.yy",},"order":0,}, - {"id":{"name":"obj_controller","path":"objects/obj_controller/obj_controller.yy",},"order":5,}, - {"id":{"name":"scr_discordFunctions","path":"scripts/scr_discordFunctions/scr_discordFunctions.yy",},"order":6,}, - {"id":{"name":"scr_discordSystemFunctions","path":"scripts/scr_discordSystemFunctions/scr_discordSystemFunctions.yy",},"order":1,}, + {"id":{"name":"obj_discordController","path":"objects/obj_discordController/obj_discordController.yy",},"order":2,}, + {"id":{"name":"scr_discordMessageEmbeds","path":"scripts/scr_discordMessageEmbeds/scr_discordMessageEmbeds.yy",},"order":8,}, + {"id":{"name":"scr_discordEnums","path":"scripts/scr_discordEnums/scr_discordEnums.yy",},"order":5,}, + {"id":{"name":"obj_controller","path":"objects/obj_controller/obj_controller.yy",},"order":2,}, + {"id":{"name":"scr_discordOtherClasses","path":"scripts/scr_discordOtherClasses/scr_discordOtherClasses.yy",},"order":9,}, {"id":{"name":"scr_stuff","path":"scripts/scr_stuff/scr_stuff.yy",},"order":0,}, + {"id":{"name":"scr_discordSystemFunctions","path":"scripts/scr_discordSystemFunctions/scr_discordSystemFunctions.yy",},"order":1,}, + {"id":{"name":"scr_discordConfig","path":"scripts/scr_discordConfig/scr_discordConfig.yy",},"order":4,}, {"id":{"name":"scr_discordSystemEnums","path":"scripts/scr_discordSystemEnums/scr_discordSystemEnums.yy",},"order":0,}, - {"id":{"name":"scr_discordEnums","path":"scripts/scr_discordEnums/scr_discordEnums.yy",},"order":5,}, - {"id":{"name":"scr_discordMessageComponents","path":"scripts/scr_discordMessageComponents/scr_discordMessageComponents.yy",},"order":7,}, - {"id":{"name":"scr_discordBot","path":"scripts/scr_discordBot/scr_discordBot.yy",},"order":3,}, {"id":{"name":"scr_fileSystem","path":"scripts/scr_fileSystem/scr_fileSystem.yy",},"order":2,}, - {"id":{"name":"scr_discordConfig","path":"scripts/scr_discordConfig/scr_discordConfig.yy",},"order":4,}, - {"id":{"name":"obj_discordController","path":"objects/obj_discordController/obj_discordController.yy",},"order":2,}, - {"id":{"name":"scr_discordOtherClasses","path":"scripts/scr_discordOtherClasses/scr_discordOtherClasses.yy",},"order":8,}, - {"id":{"name":"rm_test","path":"rooms/rm_test/rm_test.yy",},"order":6,}, + {"id":{"name":"scr_discordFunctions","path":"scripts/scr_discordFunctions/scr_discordFunctions.yy",},"order":6,}, + {"id":{"name":"rm_test","path":"rooms/rm_test/rm_test.yy",},"order":3,}, ], "Options": [ {"name":"Linux","path":"options/linux/options_linux.yy",}, @@ -35,10 +36,10 @@ {"roomId":{"name":"rm_test","path":"rooms/rm_test/rm_test.yy",},}, ], "Folders": [ - {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Extensions","folderPath":"folders/Extensions.yy","order":3,}, - {"resourceType":"GMFolder","resourceVersion":"1.0","name":"GMDiscord","folderPath":"folders/GMDiscord.yy","order":4,}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Extensions","folderPath":"folders/Extensions.yy","order":1,}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Scripts","folderPath":"folders/Scripts.yy","order":0,}, + {"resourceType":"GMFolder","resourceVersion":"1.0","name":"GMDiscord","folderPath":"folders/GMDiscord.yy","order":5,}, {"resourceType":"GMFolder","resourceVersion":"1.0","name":"System","folderPath":"folders/GMDiscord/System.yy","order":1,}, - {"resourceType":"GMFolder","resourceVersion":"1.0","name":"Scripts","folderPath":"folders/Scripts.yy","order":1,}, ], "AudioGroups": [ {"resourceType":"GMAudioGroup","resourceVersion":"1.3","name":"audiogroup_default","targets":-1,}, @@ -46,7 +47,9 @@ "TextureGroups": [ {"resourceType":"GMTextureGroup","resourceVersion":"1.3","name":"Default","isScaled":true,"compressFormat":"bz2","loadType":"default","directory":"","autocrop":true,"border":2,"mipsToGenerate":0,"groupParent":null,"targets":-1,}, ], - "IncludedFiles": [], + "IncludedFiles": [ + {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"cockroach.jpg","CopyToMask":-1,"filePath":"datafiles",}, + ], "MetaData": { "IDEVersion": "2022.0.1.31", }, diff --git a/datafiles/cockroach.jpg b/datafiles/cockroach.jpg new file mode 100644 index 0000000..15076f1 Binary files /dev/null and b/datafiles/cockroach.jpg differ diff --git a/objects/obj_controller/Create_0.gml b/objects/obj_controller/Create_0.gml index ebf4669..b9cbd2e 100644 --- a/objects/obj_controller/Create_0.gml +++ b/objects/obj_controller/Create_0.gml @@ -1,20 +1,15 @@ myBot = new discordBot(global.config.errorBotToken, global.config.errorApplicationId, true); -// Create a simple slash command where the user types /ping -var _testGuildCommand = new discordGuildCommand("ping", "Just a test.", DISCORD_COMMAND_TYPE.chatInput); - -//myBot.guildCommandCreate(global.config.serverId, _testGuildCommand, function(){ -// show_debug_message(json_parse(async_load[? "result"])); -//}); - var _guildId = "1090453953482866738"; -var _userId = "1101162577725227108" +var _userId = "1101162577725227108"; +var _channelId = "1113607940977463387" // Define the callback function to handle the response var _callback = function() { - show_debug_message("The request responded!"); + show_debug_message("Message with an attachment sent!"); }; -// Call the method -myBot.guildChannelsGet(_guildId, _callback); +var _fileAttachment = new discordFileAttachment("C:/Users/madma/OneDrive/Desktop/Cockroach-05-1802025153.jpg", "cockroach.jpg", "A cockroach"); +// Call the method +myBot.messageEdit(_channelId, "1114011904147468400", "Editeadsfgsadddddd", _callback); \ No newline at end of file diff --git a/objects/obj_controller/KeyPress_32.gml b/objects/obj_controller/KeyPress_32.gml new file mode 100644 index 0000000..bb67c1f --- /dev/null +++ b/objects/obj_controller/KeyPress_32.gml @@ -0,0 +1,20 @@ +var _guildId = "1090453953482866738"; +var _userId = "1101162577725227108"; +var _channelId = "1113607940977463387" + +var _callback = function(_response) { + var _roles = _response; + + // Process the roles data + for (var i = 0; i < array_length(_roles); i++) { + var _role = _roles[i]; + var _roleId = _role.id; + var _roleName = _role.name; + + show_debug_message("Role ID: " + _roleId); + show_debug_message("Role Name: " + _roleName); + } +}; + +// Call the method +myBot.guildRolesGet(_guildId, _callback); \ No newline at end of file diff --git a/objects/obj_controller/Keyboard_32.gml b/objects/obj_controller/Keyboard_32.gml deleted file mode 100644 index e69de29..0000000 diff --git a/objects/obj_controller/obj_controller.yy b/objects/obj_controller/obj_controller.yy index a026ceb..5f91f77 100644 --- a/objects/obj_controller/obj_controller.yy +++ b/objects/obj_controller/obj_controller.yy @@ -23,7 +23,7 @@ "physicsShapePoints": [], "eventList": [ {"resourceType":"GMEvent","resourceVersion":"1.0","name":"","isDnD":false,"eventNum":0,"eventType":0,"collisionObjectId":null,}, - {"resourceType":"GMEvent","resourceVersion":"1.0","name":"","isDnD":false,"eventNum":32,"eventType":5,"collisionObjectId":null,}, + {"resourceType":"GMEvent","resourceVersion":"1.0","name":"","isDnD":false,"eventNum":32,"eventType":9,"collisionObjectId":null,}, ], "properties": [], "overriddenProperties": [], diff --git a/objects/obj_discordController/Create_0.gml b/objects/obj_discordController/Create_0.gml index 5d53bd8..25d4b36 100644 --- a/objects/obj_discordController/Create_0.gml +++ b/objects/obj_discordController/Create_0.gml @@ -1,6 +1,6 @@ requestCallbacks = []; botArray = []; -global.lastMessageTest = -1; + diff --git a/objects/obj_discordController/KeyPress_32.gml b/objects/obj_discordController/KeyPress_32.gml deleted file mode 100644 index e69de29..0000000 diff --git a/objects/obj_discordController/Other_68.gml b/objects/obj_discordController/Other_68.gml index e756f43..15ad9f9 100644 --- a/objects/obj_discordController/Other_68.gml +++ b/objects/obj_discordController/Other_68.gml @@ -32,21 +32,25 @@ repeat(array_length(obj_discordController.botArray)){ case network_type_data: var _receivedData = discord_gateWay_event_parse(); - // Store the sequence number if it exists - if (_receivedData.s != pointer_null) { - var _tester = 0; - _currentBot.__gatewaySequenceNumber = _receivedData.s; - } - // Event Handling switch (_receivedData.op){ //This event is the first event that is received. For setting up an initial heartbeat with the gateway case DISCORD_GATEWAY_OP_CODE.hello: + if (_currentBot.__gatewayReconnect){ + _currentBot.__gatewaySendResume(); + } + _currentBot.__gatewaySendHeartbeat(); var _heartbeatInterval = floor(_receivedData.d.heartbeat_interval / 1000); // Convert ms to seconds - __discordTrace("Heartbeat interval: " + string(_heartbeatInterval)); + __discordTrace("Hello - Heartbeat interval - Every " + string(_heartbeatInterval) + " seconds"); + + //Make sure a heartbeat timesource doesn't already exist + if (time_source_exists(_currentBot.__gatewayHeartbeatTimeSource)){ + time_source_destroy(_currentBot.__gatewayHeartbeatTimeSource); + } + //Create a Time Source to send heartbeats - var _heartbeatTimeSource = time_source_create( + _currentBot.__gatewayHeartbeatTimeSource = time_source_create( time_source_global, _heartbeatInterval, time_source_units_seconds, @@ -54,7 +58,7 @@ repeat(array_length(obj_discordController.botArray)){ [], -1 ); - time_source_start(_heartbeatTimeSource); + time_source_start(_currentBot.__gatewayHeartbeatTimeSource); break; //Discord acknowledges each heartbeat sent over the gateway @@ -70,14 +74,30 @@ repeat(array_length(obj_discordController.botArray)){ //attempt to reconnect and resume case DISCORD_GATEWAY_OP_CODE.reconnect: - __discordTrace("Reconnect required, attempting..."); - network_destroy(_currentBot.__gatewaySocket); - _currentBot.__gatewaySocket = network_create_socket_ext(network_socket_wss, 443); - _currentBot.__gatewayConnection = network_connect_raw_async(_currentBot.__gatewaySocket, _currentBot.__gatewayResumeUrl + "?v=10&encoding=json", 443); + __discordTrace("Reconnect required, attempting..."); + __discord_gateway_reconnect(_currentBot); + break; + + case DISCORD_GATEWAY_OP_CODE.invalidSession: + __discordTrace("Session invalid, reconnecting..."); + var _sessionIsResumable = _receivedData.d; + + if (_sessionIsResumable){ + __discordTrace("Session invalid is resumable, attempting to resume session"); + __discord_gateway_reconnect(_currentBot); + }else{ + __discordTrace("Session invalid is NOT resumable, creating new connection"); + __discord_gateway_new_connection(_currentBot); + } break; //Identity handshakes and normal gateway events sent from your discord app case DISCORD_GATEWAY_OP_CODE.dispatch: + // Store the sequence number if it exists + if (_receivedData.s != pointer_null) { + _currentBot.__gatewaySequenceNumber = _receivedData.s; + } + if (is_string(_receivedData.t)){ var _eventName = _receivedData.t; var _eventData = _receivedData.d; diff --git a/objects/obj_discordController/Step_0.gml b/objects/obj_discordController/Step_0.gml deleted file mode 100644 index e69de29..0000000 diff --git a/objects/obj_discordController/obj_discordController.yy b/objects/obj_discordController/obj_discordController.yy index 994bbe9..3abdce3 100644 --- a/objects/obj_discordController/obj_discordController.yy +++ b/objects/obj_discordController/obj_discordController.yy @@ -24,10 +24,7 @@ "eventList": [ {"resourceType":"GMEvent","resourceVersion":"1.0","name":"","isDnD":false,"eventNum":0,"eventType":0,"collisionObjectId":null,}, {"resourceType":"GMEvent","resourceVersion":"1.0","name":"","isDnD":false,"eventNum":62,"eventType":7,"collisionObjectId":null,}, - {"resourceType":"GMEvent","resourceVersion":"1.0","name":"","isDnD":false,"eventNum":0,"eventType":3,"collisionObjectId":null,}, {"resourceType":"GMEvent","resourceVersion":"1.0","name":"","isDnD":false,"eventNum":68,"eventType":7,"collisionObjectId":null,}, - {"resourceType":"GMEvent","resourceVersion":"1.0","name":"","isDnD":false,"eventNum":32,"eventType":9,"collisionObjectId":null,}, - {"resourceType":"GMEvent","resourceVersion":"1.0","name":"","isDnD":false,"eventNum":0,"eventType":12,"collisionObjectId":null,}, ], "properties": [], "overriddenProperties": [], diff --git a/rooms/rm_test/RoomCreationCode.gml b/rooms/rm_test/RoomCreationCode.gml new file mode 100644 index 0000000..a0ac006 --- /dev/null +++ b/rooms/rm_test/RoomCreationCode.gml @@ -0,0 +1,2 @@ +instance_create_depth(0, 0, 0, obj_discordController); +instance_create_depth(0, 0, 0, obj_controller); \ No newline at end of file diff --git a/rooms/rm_test/rm_test.yy b/rooms/rm_test/rm_test.yy index b5f6cf0..452e3a4 100644 --- a/rooms/rm_test/rm_test.yy +++ b/rooms/rm_test/rm_test.yy @@ -16,19 +16,13 @@ {"inherit":false,"visible":false,"xview":0,"yview":0,"wview":1366,"hview":768,"xport":0,"yport":0,"wport":1366,"hport":768,"hborder":32,"vborder":32,"hspeed":-1,"vspeed":-1,"objectId":null,}, ], "layers": [ - {"resourceType":"GMRInstanceLayer","resourceVersion":"1.0","name":"Instances","instances":[ - {"resourceType":"GMRInstance","resourceVersion":"1.0","name":"inst_719DCD37","properties":[],"isDnd":false,"objectId":{"name":"obj_discordController","path":"objects/obj_discordController/obj_discordController.yy",},"inheritCode":false,"hasCreationCode":false,"colour":4294967295,"rotation":0.0,"scaleX":1.0,"scaleY":1.0,"imageIndex":0,"imageSpeed":1.0,"inheritedItemId":null,"frozen":false,"ignore":false,"inheritItemSettings":false,"x":32.0,"y":0.0,}, - {"resourceType":"GMRInstance","resourceVersion":"1.0","name":"inst_56E16AFE","properties":[],"isDnd":false,"objectId":{"name":"obj_controller","path":"objects/obj_controller/obj_controller.yy",},"inheritCode":false,"hasCreationCode":false,"colour":4294967295,"rotation":0.0,"scaleX":1.0,"scaleY":1.0,"imageIndex":0,"imageSpeed":1.0,"inheritedItemId":null,"frozen":false,"ignore":false,"inheritItemSettings":false,"x":0.0,"y":0.0,}, - ],"visible":true,"depth":0,"userdefinedDepth":false,"inheritLayerDepth":false,"inheritLayerSettings":false,"inheritVisibility":true,"inheritSubLayers":true,"gridX":32,"gridY":32,"layers":[],"hierarchyFrozen":false,"effectEnabled":true,"effectType":null,"properties":[],}, + {"resourceType":"GMRInstanceLayer","resourceVersion":"1.0","name":"Instances","instances":[],"visible":true,"depth":0,"userdefinedDepth":false,"inheritLayerDepth":false,"inheritLayerSettings":false,"inheritVisibility":true,"inheritSubLayers":true,"gridX":32,"gridY":32,"layers":[],"hierarchyFrozen":false,"effectEnabled":true,"effectType":null,"properties":[],}, {"resourceType":"GMRBackgroundLayer","resourceVersion":"1.0","name":"Background","spriteId":null,"colour":4278190080,"x":0,"y":0,"htiled":false,"vtiled":false,"hspeed":0.0,"vspeed":0.0,"stretch":false,"animationFPS":15.0,"animationSpeedType":0,"userdefinedAnimFPS":false,"visible":true,"depth":100,"userdefinedDepth":false,"inheritLayerDepth":false,"inheritLayerSettings":false,"inheritVisibility":true,"inheritSubLayers":true,"gridX":32,"gridY":32,"layers":[],"hierarchyFrozen":false,"effectEnabled":true,"effectType":null,"properties":[],}, ], "inheritLayers": false, - "creationCodeFile": "", + "creationCodeFile": "${project_dir}/rooms/rm_test/RoomCreationCode.gml", "inheritCode": false, - "instanceCreationOrder": [ - {"name":"inst_719DCD37","path":"rooms/rm_test/rm_test.yy",}, - {"name":"inst_56E16AFE","path":"rooms/rm_test/rm_test.yy",}, - ], + "instanceCreationOrder": [], "inheritCreationOrder": false, "sequenceId": null, "roomSettings": { diff --git a/scripts/scr_discordBot/scr_discordBot.gml b/scripts/scr_discordBot/scr_discordBot.gml index 29ad7c0..eee38e3 100644 --- a/scripts/scr_discordBot/scr_discordBot.gml +++ b/scripts/scr_discordBot/scr_discordBot.gml @@ -56,7 +56,7 @@ function discordBot(_botToken, _applicationId, _useGatewayEvents = false) constr // Find any instances of attachment URLs in the embeds var _authorAttachments = __discord_find_attachments(_embeds, "author", "icon_url", _files); var _footerAttachments = __discord_find_attachments(_embeds, "footer", "icon_url", _files); - var _attachments = array_merge(_authorAttachments, _footerAttachments); + var _attachments = __discord_array_merge(_authorAttachments, _footerAttachments); // Add attachments to the _bodyData struct if (array_length(_attachments) > 0) { @@ -75,49 +75,8 @@ function discordBot(_botToken, _applicationId, _useGatewayEvents = false) constr if (_tts){ variable_struct_set(_bodyData, "tts", true); } - - // Create the multipart/form-data body content - var _body = ""; - - if (variable_struct_exists(_bodyData, "content") || variable_struct_exists(_bodyData, "components") || variable_struct_exists(_bodyData, "embeds") || variable_struct_exists(_bodyData, "stickerIds")){ - _body += "--" + _boundary + "\r\n"; - _body += "Content-Disposition: form-data; name=\"payload_json\"\r\n"; - _body += "Content-Type: application/json\r\n\r\n"; - _body += json_stringify(_bodyData) + "\r\n"; - } else { - show_debug_message("From .messageSend: No message data was given to send"); - return; - } - - // Add files to the multipart/form-data body - if (_files != -1 && is_array(_files)){ - var _i = 0; - var _filesArrayLength = array_length(_files); - - repeat(_filesArrayLength){ - var _currentFile = _files[_i]; - var _fileBuffer = buffer_load(_currentFile.__filePath); - var _fileBase64 = buffer_base64_encode(_fileBuffer, 0, buffer_get_size(_fileBuffer)); - buffer_delete(_fileBuffer); - - _body += "--" + _boundary + "\r\n"; - _body += "Content-Disposition: form-data; name=\"files[" + string(_i) + "]\"; filename=\"" + _currentFile.__fileName + "\"\r\n"; - _body += "Content-Type: " + "image/png" + "\r\n"; - _body += "Content-Transfer-Encoding: base64\r\n\r\n"; - _body += _fileBase64 + "\r\n"; - - _i++; - } - } - _body += "--" + _boundary + "--\r\n"; - - // Send the HTTP request - var _requestId = http_request(_url, "POST", _headers, _body); - __discord_add_request_to_sent(_requestId, _callback); - - // Cleanup - ds_map_destroy(_headers); + __discord_send_http_request_multipart("channels/" + _channelId + "/messages", "POST", _bodyData, _files, __botToken, _callback); } #endregion @@ -176,48 +135,7 @@ function discordBot(_botToken, _applicationId, _useGatewayEvents = false) constr variable_struct_set(_bodyData, "attachments", _attachments); } - // Create the multipart/form-data body content - var _body = ""; - - if (variable_struct_exists(_bodyData, "content") || variable_struct_exists(_bodyData, "components") || variable_struct_exists(_bodyData, "embeds") || variable_struct_exists(_bodyData, "attachments")){ - _body += "--" + _boundary + "\r\n"; - _body += "Content-Disposition: form-data; name=\"payload_json\"\r\n"; - _body += "Content-Type: application/json\r\n\r\n"; - _body += json_stringify(_bodyData) + "\r\n"; - } else { - show_debug_message("From .messageEdit: No message data was given to edit"); - return; - } - - // Add files to the multipart/form-data body - if (_files != -1 && is_array(_files)){ - var _i = 0; - var _filesArrayLength = array_length(_files); - - repeat(_filesArrayLength){ - var _currentFile = _files[_i]; - var _fileBuffer = buffer_load(_currentFile.__filePath); - var _fileBase64 = buffer_base64_encode(_fileBuffer, 0, buffer_get_size(_fileBuffer)); - buffer_delete(_fileBuffer); - - _body += "--" + _boundary + "\r\n"; - _body += "Content-Disposition: form-data; name=\"files[" + string(_i) + "]\"; filename=\"" + _currentFile.__fileName + "\"\r\n"; - _body += "Content-Type: " + "image/png" + "\r\n"; - _body += "Content-Transfer-Encoding: base64\r\n\r\n"; - _body += _fileBase64 + "\r\n"; - - _i++; - } - } - - _body += "--" + _boundary + "--\r\n"; - - // Send the HTTP request - var _requestId = http_request(_url, "PATCH", _headers, _body); - __discord_add_request_to_sent(_requestId, _callback); - - // Cleanup - ds_map_destroy(_headers); + __discord_send_http_request_multipart("channels/" + _channelId + "/messages/" + _messageId, "PATCH", _bodyData, _files, __botToken, _callback); } @@ -348,7 +266,7 @@ function discordBot(_botToken, _applicationId, _useGatewayEvents = false) constr /// @param {string} messageId The id of the message to add the reaction to /// @param {string} emoji The emoji to use for the reaction static messageReactionCreate = function(_channelId, _messageId, _emoji, _callback = -1) { - var _urlEnpoint = "channels/" + _channelId + "/messages/" + _messageId + "/reactions/" + __url_encode(_emoji) + "/@me"; + var _urlEnpoint = "channels/" + _channelId + "/messages/" + _messageId + "/reactions/" + __discord_url_encode(_emoji) + "/@me"; __discord_send_http_request_standard(_urlEnpoint, "PUT", -1, __botToken, _callback); } @@ -363,7 +281,7 @@ function discordBot(_botToken, _applicationId, _useGatewayEvents = false) constr /// @param {string} emoji The url-encoded emoji to remove /// @param {function} callback The function to execute for the request's response. Default: -1 static messageReactionDelete = function(_channelId, _messageId, _emoji, _callback = -1){ - __discord_send_http_request_standard("channels/" + _channelId + "/messages/" + _messageId + "/reactions/" + __url_encode(_emoji) + "/@me", "DELETE", -1, __botToken, _callback); + __discord_send_http_request_standard("channels/" + _channelId + "/messages/" + _messageId + "/reactions/" + __discord_url_encode(_emoji) + "/@me", "DELETE", -1, __botToken, _callback); } #endregion @@ -608,6 +526,67 @@ function discordBot(_botToken, _applicationId, _useGatewayEvents = false) constr #endregion + #region guildRolesGet(guildId, [callback]) + + /// @func guildRolesGet(guildId, [callback]) + /// @desc Fetches the roles of a server + /// @param {string} guildId The id of the guild (server) from which to fetch the roles + /// @param {function} callback The function to execute for the request's response. Default: -1 + static guildRolesGet = function(_guildId, _callback = -1){ + var _urlEndpoint = "guilds/" + _guildId + "/roles"; + __discord_send_http_request_standard(_urlEndpoint, "GET", -1, __botToken, _callback); + } + + #endregion + + #region guildRoleCreate(guildId, [callback]) + + /// @func guildRoleCreate(guildId, [callback]) + /// @desc Creates a new role in a server + /// @param {string} guildId The id of the guild (server) where the role will be created + /// @param {function} callback The function to execute for the request's response. Default: -1 + /// @param {string} name The name of the role + /// @param {string} permissions bitwise value of the enabled/disabled permissions + /// @param {real} color bitwise value of the enabled/disabled permissions + /// @param {bool} hoist whether the role should be displayed separately in the sidebar + /// @param {any} icon the role's icon image (if the guild has the ROLE_ICONS feature) + /// @param {string} unicodeEmoji the role's unicode emoji as a standard emoji (if the guild has the ROLE_ICONS feature) + /// @param {bool} mentionable whether the role should be mentionable + static guildRoleCreate = function(_guildId, _callback = -1, _name = "new role", _permissions = -1, _color = 0, _hoist = false, _icon = pointer_null, _unicodeEmoji = pointer_null, _mentionable = false){ + var _urlEndpoint = "guilds/" + _guildId + "/roles"; + + var _bodyData = { + name: _name, + color: _color, + hoist: _hoist, + icon: _icon, + unicode_emoji: _unicodeEmoji, + mentionable: _mentionable + } + + if (_permissions != -1){ + _bodyData[$ "permissions"] = _permissions; + } + + __discord_send_http_request_standard(_urlEndpoint, "POST", _bodyData, __botToken, _callback); + } + + #endregion + + #region guildRoleDelete(guildId, roleId, [callback]) + + /// @func guildRoleDelete(guildId, roleId, [callback]) + /// @desc Deletes a role from a server + /// @param {string} guildId The id of the guild (server) where the role will be deleted + /// @param {string} roleId The id of the role to be deleted + /// @param {function} callback The function to execute for the request's response. Default: -1 + static guildRoleDelete = function(_guildId, _roleId, _callback = -1){ + var _urlEndpoint = "guilds/" + _guildId + "/roles/" + _roleId; + __discord_send_http_request_standard(_urlEndpoint, "DELETE", -1, __botToken, _callback); + } + + #endregion + #region triggerTypingIndicator(channelId, [callback]) /// @func triggerTypingIndicator(channelId, [callback]) @@ -624,23 +603,22 @@ function discordBot(_botToken, _applicationId, _useGatewayEvents = false) constr #region Gateway event functions //Set up gateway events - if (_useGatewayEvents){ - var _url = "wss://gateway.discord.gg/?v=10&encoding=json"; - __gatewaySocket = network_create_socket_ext(network_socket_wss, 443); - __gatewayConnection = network_connect_raw_async(__gatewaySocket, _url, 443); - }else{ - __gatewaySocket = -1; - __gatewayConnection = -1; - } - + __gatewaySocket = -1; + __gatewayConnection = -1; __gatewayHeartbeatCounter = 0; + __gatewayHeartbeatTimeSource = -1; __gatewayIndentityHandshake = false; __gatewaySequenceNumber = -1; __gatewayResumeUrl = ""; __gatewaySessionId = "" __gatewayNumberOfDisconnects = 0; + __gatewayReconnect = false; gatewayEventCallbacks = {}; + if (_useGatewayEvents){ + __discord_gateway_new_connection(self); + } + #region interactionResponseSend(interactionId, interactionToken, callbackType, [content], [callback], [components], [embeds], [tts]) /// @func interactionResponseSend(interactionId, interactionToken, callbackType, [content], [callback], [components], [embeds], [tts]) @@ -756,7 +734,7 @@ function discordBot(_botToken, _applicationId, _useGatewayEvents = false) constr #region interactionResponseFollowUp(interactionToken, content, [callback], [components], [embeds], [attachments], [files]) - /// @func interactionResponseFollowUp(applicationId, interactionToken, content, [callback], [components], [embeds], [attachments], [files]) + /// @func interactionResponseFollowUp(interactionToken, content, [callback], [components], [embeds], [attachments], [files]) /// @desc Sends a new follow-up message to an Interaction. Must include a message. /// @param {string} interactionToken The token for the Interaction /// @param {string} content The new message content (Up to 2000 characters) @@ -845,22 +823,21 @@ function discordBot(_botToken, _applicationId, _useGatewayEvents = false) constr if (_bytesSent > 0){ __gatewayHeartbeatCounter++; - if (!__gatewayIndentityHandshake && __gatewayHeartbeatCounter > 0){ - __gatewaySendIdentity(); + if (__gatewayHeartbeatCounter > 0){ + if (!__gatewayIndentityHandshake && !__gatewayReconnect){ + __gatewaySendIdentity(); + }else if (__gatewayReconnect){ + __gatewaySendResume(); + } } }else{ - __gatewayHeartbeatCounter = 0; - var _url = "wss://gateway.discord.gg/?v=10&encoding=json"; - network_destroy(__gatewaySocket); - __gatewaySocket = network_create_socket_ext(network_socket_wss, 443); - __gatewayConnection = network_connect_raw_async(__gatewaySocket, _url, 443); - __gatewayNumberOfDisconnects++; __discordTrace("Connection to gateway lost: reconnecting..."); + __discord_gateway_reconnect(self); } } /// @func __gatewaySendIdentity() - /// @desc after a heartbeat is established with the gateway, an indentity must be sent to finish setting up the connection + /// @desc after a heartbeat is established with the gateway, an identity must be sent to finish setting up the connection function __gatewaySendIdentity() { var _botToken = __botToken; @@ -880,13 +857,29 @@ function discordBot(_botToken, _applicationId, _useGatewayEvents = false) constr __gatewayEventSend(_payload); } + /// @func __gatewaySendResume() + /// @desc Sends a resume event for use after a disconnect + function __gatewaySendResume() { + //Send resume gateway event to discord + var _resumeData = { + op: DISCORD_GATEWAY_OP_CODE.resume, + d: { + token: __botToken, + session_id: __gatewaySessionId, + seq: int64(__gatewaySequenceNumber) + } + } + + __gatewayEventSend(_resumeData); + } + /// @func __gatewayEventSend(payloadStruct) /// @desc Takes a struct, encodes it, and sends it to the Discord event function __gatewayEventSend(_payloadStruct){ var _payloadString = json_stringify(_payloadStruct); var _payloadBuffer = buffer_create(0, buffer_grow, 1); buffer_write(_payloadBuffer, buffer_string, _payloadString); - var _payloadBufferTrimmed = __trim_buffer(_payloadBuffer); + var _payloadBufferTrimmed = __discord_gateway_trim_buffer(_payloadBuffer); //Returns the number of bytes sent or a number less than 0 if it failed var _bytesSent = network_send_raw(__gatewaySocket, _payloadBufferTrimmed, buffer_get_size(_payloadBufferTrimmed), network_send_text); buffer_delete(_payloadBufferTrimmed); diff --git a/scripts/scr_discordConfig/scr_discordConfig.gml b/scripts/scr_discordConfig/scr_discordConfig.gml index bee4430..945118e 100644 --- a/scripts/scr_discordConfig/scr_discordConfig.gml +++ b/scripts/scr_discordConfig/scr_discordConfig.gml @@ -1 +1,2 @@ +//Outputs additional debug information about GMDiscord to the console #macro DISCORD_VERBOSE true \ No newline at end of file diff --git a/scripts/scr_discordFunctions/scr_discordFunctions.gml b/scripts/scr_discordFunctions/scr_discordFunctions.gml index 717b981..dffa906 100644 --- a/scripts/scr_discordFunctions/scr_discordFunctions.gml +++ b/scripts/scr_discordFunctions/scr_discordFunctions.gml @@ -16,5 +16,15 @@ function discord_http_response_parse(){ /// @desc Parses the async_load map's "result" key in a HTTP async event which is typically the data you will be working with /// @return Struct or array function discord_http_response_result(){ - return json_parse(json_encode(async_load)).result; + var _dataStruct = json_parse(json_encode(async_load)); + + if (variable_struct_exists(_dataStruct, "result")){ + try{ + return json_parse(_dataStruct.result); + }catch(_error){ + return _dataStruct.result; + } + }else{ + return {}; + } } \ No newline at end of file diff --git a/scripts/scr_discordMessageEmbeds/scr_discordMessageEmbeds.gml b/scripts/scr_discordMessageEmbeds/scr_discordMessageEmbeds.gml new file mode 100644 index 0000000..86216a0 --- /dev/null +++ b/scripts/scr_discordMessageEmbeds/scr_discordMessageEmbeds.gml @@ -0,0 +1,187 @@ +/// @func discordMessageEmbed([title], [type], [description], [url], [timestamp], [color], [footer], [image], [thumbnail], [video], [provider], [author], [fields]) +/// @desc Creates a new Discord message embed. +/// @param {string} title - The title of the embed. +/// @param {string} type - The type of the embed (always "rich" for webhook embeds). All types: "rich", "image", "video", "gifv", "article", "link" +/// @param {string} description - The description of the embed. +/// @param {string} url - The URL of the embed. +/// @param {string} timestamp - The ISO8601 timestamp of the embed content. +/// @param {real} color - The color code of the embed. +/// @param {struct.discordMessageEmbedFooter} footer - The footer information. +/// @param {struct.discordMessageEmbedImage} image - The image information. +/// @param {struct.discordMessageEmbedThumnail} thumbnail - The thumbnail information. +/// @param {struct.discordMessageEmbedVideo} video - The video information. +/// @param {struct.discordMessageEmbedProvider} provider - The provider information. +/// @param {struct.discordMessageEmbed} author - The author information. +/// @param {Array} fields - The array of embed field structs. +function discordMessageEmbed(_title = "", _type = "rich", _description = "", _url = "", _timestamp = "", _color = -1, _footer = -1, _image = -1, _thumbnail = -1, _video = -1, _provider = -1, _author = -1, _fields = -1) constructor { + // Title of the embed + title = _title; + + // Type of the embed (always "rich" for webhook embeds) + type = _type; + + // Description of the embed + description = _description; + + // URL of the embed + if (_url != ""){ + url = _url; + } + + // ISO8601 timestamp of the embed content + if (_timestamp != ""){ + timestamp = _timestamp; + } + + // Color code of the embed + if (_color != -1){ + color = _color; + } + + // Footer information + if (_footer != -1){ + footer = _footer; + } + + // Image information + if (_image != -1){ + image = _image; + } + + // Thumbnail information + if (_thumbnail != -1){ + thumbnail = _thumbnail; + } + + // Video information + if (_video != -1){ + video = _video; + } + + // Provider information + if (_provider != -1){ + provider = _provider; + } + + // Author information + if (_author != -1){ + author = _author; + } + + // Fields information (array of embed field objects) + if (_fields != -1){ + fields = _fields; + } +} + +/// @param {string} text - The footer text. +/// @param {string} iconUrl - The URL of the footer icon. +/// @param {string} proxyIconUrl - A proxied URL of the footer icon. +function discordMessageEmbedFooter(_text = "", _iconUrl = "", _proxyIconUrl = "") constructor { + text = _text; + + if (_iconUrl != ""){ + icon_url = _iconUrl; + } + + if (_proxyIconUrl != ""){ + proxy_icon_url = _proxyIconUrl; + } +} + +/// @param {string} url - The source URL of the image. +/// @param {string} proxyUrl - A proxied URL of the image. +/// @param {real} height - The height of the image. +/// @param {real} width - The width of the image. +function discordMessageEmbedImage(_url = "", _proxyUrl = "", _height = -1, _width = -1) constructor { + if (_url != ""){ + url = _url; + } + if (_proxyUrl != ""){ + proxy_url = _proxyUrl; + } + if (_height != -1){ + height = _height; + } + if (_width != -1){ + width = _width; + } +} + +/// @param {string} url - The source URL of the thumbnail. +/// @param {string} proxyUrl - A proxied URL of the thumbnail. +/// @param {real} height - The height of the thumbnail. +/// @param {real} width - The width of the thumbnail. +function discordMessageEmbedThumbnail(_url = "", _proxyUrl = "", _height = -1, _width = -1) constructor { + if (_url != ""){ + url = _url; + } + if (_proxyUrl != ""){ + proxy_url = _proxyUrl; + } + if (_height != -1){ + height = _height; + } + if (_width != -1){ + width = _width; + } +} + + +/// @param {string} url - The source URL of the video. +/// @param {real} height - The height of the video. +/// @param {real} width - The width of the video. +function discordMessageEmbedVideo(_url = "", _height = -1, _width = -1) constructor { + if (_url != ""){ + url = _url; + } + if (_height != -1){ + height = _height; + } + if (_width != -1){ + width = _width; + } +} + +/// @param {string} name - The name of the provider. +/// @param {string} url - The URL of the provider. +function discordMessageEmbedProvider(_name = "", _url = "") constructor { + if (_name != ""){ + name = _name; + } + if (_url != ""){ + url = _url; + } +} + +/// @param {string} name - The name of the author. +/// @param {string} url - The URL of the author. +/// @param {string} iconUrl - The URL of the author icon. +/// @param {string} proxyIconUrl - A proxied URL of the author icon. +function discordMessageEmbedAuthor(_name = "", _url = "", _iconUrl = "", _proxyIconUrl = "") constructor { + if (_name != ""){ + name = _name; + } + if (_url != ""){ + url = _url; + } + if (_iconUrl != ""){ + icon_url = _iconUrl; + } + if (_proxyIconUrl != ""){ + proxy_icon_url = _proxyIconUrl; + } +} + +/// @param {string} name - The name of the field. +/// @param {string} value - The value of the field. +/// @param {bool} inline - Whether or not this field should display inline. +function discordMessageEmbedField(_name = "", _value = "", _inline = false) constructor { + name = _name; + value = _value; + inline = _inline; +} + + + + diff --git a/scripts/scr_discordMessageEmbeds/scr_discordMessageEmbeds.yy b/scripts/scr_discordMessageEmbeds/scr_discordMessageEmbeds.yy new file mode 100644 index 0000000..6525f81 --- /dev/null +++ b/scripts/scr_discordMessageEmbeds/scr_discordMessageEmbeds.yy @@ -0,0 +1,11 @@ +{ + "resourceType": "GMScript", + "resourceVersion": "1.0", + "name": "scr_discordMessageEmbeds", + "isDnD": false, + "isCompatibility": false, + "parent": { + "name": "GMDiscord", + "path": "folders/GMDiscord.yy", + }, +} \ No newline at end of file diff --git a/scripts/scr_discordOtherClasses/scr_discordOtherClasses.gml b/scripts/scr_discordOtherClasses/scr_discordOtherClasses.gml index d9d1c40..16969cd 100644 --- a/scripts/scr_discordOtherClasses/scr_discordOtherClasses.gml +++ b/scripts/scr_discordOtherClasses/scr_discordOtherClasses.gml @@ -91,82 +91,6 @@ function discordEmoji(_name, _id = pointer_null, _animated = false) constructor animated = _animated; } -/// @func discordMessageEmbed([title], [type], [description], [url], [timestamp], [color], [footer], [image], [thumbnail], [video], [provider], [author], [fields]) -/// @desc Creates a new Discord message embed. -/// @param {string} title - The title of the embed. -/// @param {string} type - The type of the embed (always "rich" for webhook embeds). All types: "rich", "image", "video", "gifv", "article", "link" -/// @param {string} description - The description of the embed. -/// @param {string} url - The URL of the embed. -/// @param {string} timestamp - The ISO8601 timestamp of the embed content. -/// @param {real} color - The color code of the embed. -/// @param {struct} footer - The footer information. Properties: "text", "icon_url" (optional). -/// @param {struct} image - The image information. Properties: "url", "height", "width" -/// @param {struct} thumbnail - The thumbnail information. Properties: "url", "height", "width" -/// @param {struct} video - The video information. Properties: "url", "proxy_url", "height", "width" -/// @param {struct} provider - The provider information. Properties: "name", and "url". -/// @param {struct} author - The author information. Properties: "name", "url" (optional), "icon_url" (optional). -/// @param {Array} fields - The array of embed field objects. Each field object has properties: "name", "value", "inline" (optional, default false). -function discordMessageEmbed(_title = "", _type = "rich", _description = "", _url = "", _timestamp = "", _color = -1, _footer = -1, _image = -1, _thumbnail = -1, _video = -1, _provider = -1, _author = -1, _fields = -1) constructor { - // Title of the embed - title = _title; - - // Type of the embed (always "rich" for webhook embeds) - type = _type; - - // Description of the embed - description = _description; - - // URL of the embed - if (_url != ""){ - url = _url; - } - - // ISO8601 timestamp of the embed content - if (_timestamp != ""){ - timestamp = _timestamp; - } - - // Color code of the embed - if (_color != -1){ - color = _color; - } - - // Footer information - if (_footer != -1){ - footer = _footer; - } - - // Image information - if (_image != -1){ - image = _image; - } - - // Thumbnail information - if (_thumbnail != -1){ - thumbnail = _thumbnail; - } - - // Video information - if (_video != -1){ - video = _video; - } - - // Provider information - if (_provider != -1){ - provider = _provider; - } - - // Author information - if (_author != -1){ - author = _author; - } - - // Fields information (array of embed field objects) - if (_fields != -1){ - fields = _fields; - } -} - /// @func discordFileAttachment(filePath, fileName, [fileDescription]) /// @desc Creates a new Discord file for sending in messages. /// @param {string} filePath - Complete filePath for file being sent diff --git a/scripts/scr_discordSystemFunctions/scr_discordSystemFunctions.gml b/scripts/scr_discordSystemFunctions/scr_discordSystemFunctions.gml index debd94d..356578c 100644 --- a/scripts/scr_discordSystemFunctions/scr_discordSystemFunctions.gml +++ b/scripts/scr_discordSystemFunctions/scr_discordSystemFunctions.gml @@ -32,47 +32,57 @@ function __discord_response_is_error(_responseJson) { } } -/// @Func __discord_error_print(json) -/// @param {string} discordResponse The JSON response from the Discord API -function __discord_error_print(_discordResponse){ - var _error = json_parse(_discordResponse); - var _errorMessageToOutput = "Something is wrong with your HTTP request!\n"; - - // Check if the main error fields exist - if (variable_struct_exists(_error, "code") && variable_struct_exists(_error, "message")) { - _errorMessageToOutput += "Error Code: " + string(_error.code) + "\n"; - _errorMessageToOutput += "Message: " + _error.message + "\n"; +// @Func __discord_error_print(json) +// @param {string} discordResponse The JSON response from the Discord API +function __discord_error_print(_jsonString) { + var _response = json_parse(_jsonString); + + if (is_struct(_response)) { + var _message = "Discord API returned an error:"; + + if(variable_struct_exists(_response, "code")) { + _message += "\nCode: " + string(_response.code); + } + + if(variable_struct_exists(_response, "message")) { + _message += "\nMessage: " + string(_response.message); + } + + if (variable_struct_exists(_response, "errors")) { + if (is_struct(_response.errors)){ + _message += "\nErrors:"; + _message += __discord_process_error_struct(_response.errors, " "); + } + } + + show_debug_message(_message); } - - // Check if the 'errors' object exists - if (variable_struct_exists(_error, "errors")) { - var _errorDetails = _error.errors; +} + +/// @param _errorStruct struct +/// @param _indentation string +function __discord_process_error_struct(_errorStruct, _indentation) { + var _result = ""; + var _nextIndentation = _indentation + " "; + var _keys = variable_struct_get_names(_errorStruct); + + for (var _i = 0; _i < array_length(_keys); ++_i) { + var _key = _keys[_i]; + var _value = _errorStruct[$ _key]; + _result += "\n" + _indentation + string(_key) + ":"; - // Iterate over the keys in the 'errors' struct - var _keys = variable_struct_get_names(_errorDetails); - for (var i = 0; i < array_length(_keys); i++) { - var _key = _keys[i]; - var _detail = variable_struct_get(_errorDetails, _key); - - // Check if the detail error fields exist - if (variable_struct_exists(_detail, "_errors")) { - var _errorsArray = _detail._errors; - - // Iterate over the errors in the '_errors' array - for (var j = 0; j < array_length(_errorsArray); j++) { - var _errorItem = _errorsArray[j]; - - // Check if the error item fields exist - if (variable_struct_exists(_errorItem, "code") && variable_struct_exists(_errorItem, "message")) { - _errorMessageToOutput += "Detail Error Code: " + string(_errorItem.code) + "\n"; - _errorMessageToOutput += "Detail Message: " + _errorItem.message + "\n"; - } - } + if (is_struct(_value)) { + _result += __discord_process_error_struct(_value, _nextIndentation); + } else if (is_array(_value)) { + for (var _j = 0; _j < array_length(_value); ++_j) { + _result += __discord_process_error_struct(_value[_j], _nextIndentation); } + } else { + _result += " " + string(_value); } } - - __discordTrace(_errorMessageToOutput); + + return _result; } /// @func __discord_add_request_to_sent(requestId,[callback]) @@ -168,11 +178,11 @@ function __discord_find_attachments(_embeds, _property, _subProperty, _files) { return _attachments; } -/// @func __url_encode(string) +/// @func __discord_url_encode(string) /// @desc URL-encodes the given string /// @param {string} string The string to be URL-encoded /// @return {string} The URL-encoded string -function __url_encode(_str) { +function __discord_url_encode(_str) { var _encodedStr = ""; var _strLength = string_length(_str); @@ -184,11 +194,11 @@ function __url_encode(_str) { _encodedStr += _char; } else { // Encode the character as a percent-encoded string - var _byteArray = __string_unicode_to_byte_array(_char); + var _byteArray = __discord_string_unicode_to_byte_array(_char); var _byteArrayLength = array_length(_byteArray); for (var _j = 0; _j < _byteArrayLength; _j++) { - var _hex = string_upper(string(__base_convert(_byteArray[_j], 10, 16))); + var _hex = string_upper(string(__discord_base_convert(_byteArray[_j], 10, 16))); if (string_length(_hex) < 2) { _hex = "0" + _hex; } @@ -200,11 +210,11 @@ function __url_encode(_str) { return _encodedStr; } -/// @func __string_unicode_to_byte_array(str) +/// @func __discord_string_unicode_to_byte_array(string) /// @desc Converts a Unicode string to a byte array using UTF-8 encoding -/// @param {string} str The Unicode string to be converted +/// @param {string} string The Unicode string to be converted /// @return {array} The byte array representing the UTF-8 encoded string -function __string_unicode_to_byte_array(_str) { +function __discord_string_unicode_to_byte_array(_str) { var _byteArray = []; var _strLength = string_length(_str); @@ -231,13 +241,13 @@ function __string_unicode_to_byte_array(_str) { return _byteArray; } -/// @func __base_convert(number, fromBase, toBase) +/// @func __discord_base_convert(number, fromBase, toBase) /// @desc Converts a number from one base to another /// @param {real} number The number to be converted /// @param {real} fromBase The base of the input number /// @param {real} toBase The base to convert the number to /// @return {string} The number converted to the target base -function __base_convert(_number, _fromBase, _toBase) { +function __discord_base_convert(_number, _fromBase, _toBase) { var _digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; var _result = ""; @@ -264,11 +274,12 @@ function __base_convert(_number, _fromBase, _toBase) { return _result; } -/// @function __trim_buffer(inputBuffer) -/// @description Trims trailing 00 bytes from a buffer +/// @func __discord_gateway_trim_buffer(inputBuffer) +/// @desc Trims trailing `00` bytes from a buffer, the Discord gateway refuses to take packets that have trailing `00` bytes. +/// Writing a string to a buffer in GML will produce these "blank" bytes at the end for some reason or maybe i'm just stupid, idk? /// @param {id.buffer} inputBuffer The buffer to trim /// @return {id.buffer} The trimmed buffer -function __trim_buffer(inputBuffer) { +function __discord_gateway_trim_buffer(inputBuffer) { var bufferSize = buffer_get_size(inputBuffer); var trimmedSize = bufferSize; @@ -347,7 +358,7 @@ function __discord_send_http_request_standard(_endpoint, _requestMethod, _reques function __discord_send_http_request_multipart(_endpoint, _requestMethod, _requestBody, _files, _botToken, _callback = -1){ // Prepare the url and headers var _baseUrl = "https://discord.com/api/v10/" + _endpoint; - var _boundary = "----GMLBoundary" + string(random(1000000000)); + var _boundary = "----GMLBoundary" + string(irandom(1000000000)); var _headers = ds_map_create(); ds_map_add(_headers, "Content-Type", "multipart/form-data; boundary=" + _boundary); ds_map_add(_headers, "Authorization", "Bot " + _botToken); @@ -392,6 +403,55 @@ function __discord_send_http_request_multipart(_endpoint, _requestMethod, _reque ds_map_destroy(_headers); } +/// @description __discord_array_merge(...) +/// @param {array} ... An arbitrary number of arrays +function __discord_array_merge() { + var merged = []; + + for (var i = 0; i < argument_count; ++i) { + var current_array = argument[i]; + + if (is_array(current_array)) { + for (var j = 0; j < array_length(current_array); ++j) { + array_push(merged, current_array[j]); + } + } + } + + return merged; +} + +/// @desc Establish a new connection to the gateway +function __discord_gateway_new_connection(_bot){ + var _gatewayUrl = "wss://gateway.discord.gg/?v=10&encoding=json"; + + with(_bot){ + if (__gatewaySocket != -1){ + network_destroy(__gatewaySocket); + __gatewayNumberOfDisconnects++; + } + + __heartbeatCounter = 0; + __gatewaySocket = network_create_socket(network_socket_wss); + __gatewayConnection = network_connect_raw_async(__gatewaySocket, _gatewayUrl, 443); + __gatewayReconnect = false; + __gatewayIndentityHandshake = false; + } +} + +/// @desc Resume a previous gateway session after a disconnect or invalid session +function __discord_gateway_reconnect(_bot){ + with(_bot){ + network_destroy(__gatewaySocket); + __gatewayNumberOfDisconnects++; + __heartbeatCounter = 0; + __gatewaySocket = network_create_socket(network_socket_wss); + __gatewayConnection = network_connect_raw_async(__gatewaySocket, __gatewayResumeUrl, 443); + __gatewayReconnect = true; + } +} + +