------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- I MADE THIS! This set of code was made by me, MaddeMichael (twitter.com/MaddeMichael). You're allowed to do what ever you want with it in personal or commercial projects. Just please don't redistribute it! It's better to link back to me, especially if people need help with anything! (Since I made it, I know it pretty well and can fix stuff pretty quickly) This manual is aimed primarily for GMS2. While a version of this extension exists for GM:S 1.4, it will not be maintained or updated. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- General Overview: This set of assets is designed to provide an easy way to connect a game to Twitch.tv chat to make fun, interactive games that "chatters" can join in on. Of course, it's uses aren't limited to that! As nearly everything is written in pure GML, it should theoretically function on all export targets (if not, it's probably a Gamemaker bug!). The only thing not in GML is a small JS extension to add HTML5 support. The features are designed to be simple and easy to use, and work in both blocking and non-blocking connect modes. For quick setup, the only functions you need to worry about are: twitch_connect(oauth, username, channel) twitch_set_callback_script(script) twitch_send_chat_message(message) twitch_disconnect() Though it's useful to also take note of the enum "public_twitch_message_struct" to handle incoming data (the most important properties are probably 'username', 'command' and 'message') Things marked with double underscores (e.g., __TWITCH_SERVER_URL) are mostly used for internal stuff and shouldn't be messed with directly, unless you really know what you are doing. Things marked with "public_*" (both lowercase and uppercase) e.g., 'public_twitch_message_struct' are things that you are allowed to use, but are usually read-only. Anything starting immediately with "twitch_" should be fine to use wherever. We also use some "global pragma" initialisation of some global variables to make stuff work more reliably (If you don't know what this means, don't worry about it!) All global stuff (macros, enums, globals etc) is initialized/stored in the twitch_connect script, if you need to check any of it for whatever reason. If the Twitch manager receives no messages for over 12 minutes after connection, it will automatically disconnect. This is because Twitch automatically disconnects bots after about 10 minutes if they do not respond to PONGs. This should not interfere with normal usage and, by default, the Twitch Manager will automatically try to reconnect. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Included Objects: __TWITCH___TWITCH_obj_twitchManager This is the only object included in this thing. You should never need to worry about what it does, but it essentially just stores and manages important data, as well as asynchronous events. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Included (public) Constants Macros: Capability requests - Used with twitch_set_cap_requests to set capabilities you want your game to request - see "https://dev.twitch.tv/docs/irc#twitch-specific-irc-capabilities" for more info PUBLIC_TWITCH_CAP_MEMBERSHIP - "twitch.tv/membership" PUBLIC_TWITCH_CAP_TAGS - "twitch.tv/tags" PUBLIC_TWITCH_CAP_COMMANDS - "twitch.tv/commands" PUBLIC_TWITCH_CAP_ALL - "twitch.tv/membership twitch.tv/tags twitch.tv/commands" PUBLIC_TWITCH_CAP_CHATROOM - "twitch.tv/tags twitch.tv/commands" Enums: public_twitch_message_struct - used to access certain values in "twitch_objects" (Specific arrays that act as special data structures). Possible values are: username - When used to access a twitch_object - contains the assosiated username or 'undefined' if none specified command - When used to access a twitch_object - contains the command name or 'undefined' if none specified channel - When used to access a twitch_object - contains the source channel name (including a starting '#') or 'undefined' if none specified (Contains command parameters for commands such as CAP - e.g., "* ACK") message - When used to access a twitch_object - the message sent with the command or 'undefined' if none specified tags - When used to access a twitch_object - user tags attached to the command (an array) or 'undefined' if none specified original - When used to access a twitch_object - The original string version of the command that was converted into this object MAX - the maximum value, used for array sizing and iteration over twitch_objects parameters - When used to access a twitch_object - Contains command parameters for commands such as CAP - e.g., "* ACK") or 'undefined' if none specified - (Contains the source channel name (including a starting '#') for commands such as JOIN) - same value as "channel", but included for code readability public_twitch_disconnect_reasons - passed to disconnect callback scripts (set with "twitch_set_disconnect_callback_script(script)" to help handle unexpected disconnects no_reason - No reason is provided for the disconnect no_internet - Automatically disconncted due to no internet no_server_response - The server didn't respond recently enough, so a disconnect was made connection_failed - Initial connection failed server_requests_reconnect - The server asked the client to reconnect, so the system disconnected authentication_failed - Login authentication failed, so we disconnected twitch_manager_already_exists - Someone attempted to create another _TWITCH_obj_twitchManager, which is illegal. public_twitch_connection_status - used in conjunction with "twitch_check_connected()" to identify current connection status connected (1) - The system is currently connected and logged in to Twitch not_connected (0) - The system is not connected to Twitch attempting_connection (-1) - The system is connected to Twitch, but not currently properly logged in ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Included Scripts: twitch_connect(oauth_key, username, *channel_name) This function is a super easy way to connect to the Twitch IRC server (the way we can send and receive twitch messages, basically) Ideally, this script should be called after setup setup scripts such as "twitch_set_cap_requests", "twitch_set_callback_script", "twitch_enable_debug_output" and "twitch_set_non_blocking_connect_callback" ARGUMENTS [oauthKey] The first argument is your oauth key including the "oauth:" bit (you can get one here for quick usage: https://twitchapps.com/tmi/) [UserName] The second is your assosiated username (for the account you authorised the oauth key for) [*ChannelName] The last (optional) argument is the target channel name to connect to. If this argument is not supplied, it will connect to your own channel using your username. The hash character at the start of a channel name is optional for this function RETURNS {real} One of several values. Each have a different meaning (but less than 1 is a connection failure) 1 - The connection was immediately successful and the login info has been sent (or connection was previously established). Capability acknowledgements will arrive soon. 2 - The connection may be successful - this only occurs if a non-blocking connect callback is specified (more on this later - it's advanced usage) 0 - All other results failed to occur, so connection probably failed (yes, this really is just a backup catch) -1 - The socket was unable to be created -2 - The socket was successfully created, but failed to connect to the server -3 - There is no internet connection -4 - Auto-reconnect is enabled and active. Technically not a failure, but since connnection may not yet succeed, we assume the worst ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- twitch_disconnect(*enum reason, *text reason) Disconnects the current Twitch Session. If optional argument 'text reason' is supplied, the reason is output in the console IF debug output is enabled The optional argument "enum reason" is usually a "public_twitch_disconnect_reasons" value, which can be passed to a callback script set with "twitch_set_disconnect_callback_script" to help handle unexpected disconnects. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- twitch_check_connected() Used to determine if the game is actually connected to the Twitch Chat and logged in. RETURNS {real} - A value from "public_twitch_connection_status". Is equivelant to "true" if connected, and "false" if not connected. (0 for not connected, 1 for connected, and -1 for socket connected, but not logged in) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- twitch_send_chat_message(message) If the Twitch manager exists, this will enqueue a chat message to be sent to the connected channel without you needing to worry about terminating characters, channels or command names. If connection is lost before this command is sent, it will be queued for when connection returns (it uses "twitch_queue_command", but simplifies the chat process) RETURNS {real} - 'true' if the message was correctly enqueued, 'false' if it failed (e.g, the Twitch manager does not exist) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- twitch_queue_command(command) If the Twitch manager exists, this will enqueue the command to be batched and sent at the end of the current step (or once connection resumes, if connection was previously lost) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- twitch_send_command(command) Works almost the same sa twitch_queue_command, but sends the command immediately. You shouldn't need to use this one, as it's primary purpose is for initial connection and doesn't make use of the queue on disconnect. Note this will also fail if connection has been lost, whereas twitch_queue_command will have the command wait for connection to resume. Multiple commands can be sent at once by separating them with new lines and line-feeds ("\r\n"). (NOTE: In GM:S1.4, this will not work. You instead need to use chr(13) + char(10)) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- twitch_set_callback_script(script_index) A very important script! In order to handle any data Twitch sends to your game, you must create a script to handle the data and assign it with this function. Whenever the Twitch manager receives a command, it will pass it over to this script for further handling. (Most basic handling is already managed by the manager - commands PING, PONG, 001 and, to a lesser extent, CAP and 421) The script_index argument is simply the index of your script. The user-made script must have one argument - this will be passed in as a "twitch_object" - a special array containing useful information about what twitch has sent. Specific information in the array can be accessed with the "public_twitch_message_struct" enum. For information on what sort of commands and info a twitch_object could contain, check the Twitch Dev Page here "https://dev.twitch.tv/docs/irc" This script should be used BEFORE twitch_connect(); ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- twitch_set_disconnect_callback_script(script_index) This function lets you set a handy script which can help handle unexpected disconnections. The script_index argument is simply the index of your script. The user-made script must have one argument - this will be passed in as a value from the enum "public_twitch_disconnect_reasons" This script should be used BEFORE twitch_connect(); ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- twitch_set_cap_requests(cap1, cap2, cap3, ...) Used to setup capability requests - certain capabilities are needed for a Twitch-bot to receive certain commands, data and perform certain operations. Capabilities can be grouped into one string, separated by spaces, however, ALL must pass for the capability to be acknowledged. Passing capabilities as separate arguments means it will take longer to fully connect, but capabilities can be approved individually This script should be used BEFORE twitch_connect(); I've provided some basic macros to be supplied as arguments for most cases: PUBLIC_TWITCH_CAP_MEMBERSHIP PUBLIC_TWITCH_CAP_TAGS PUBLIC_TWITCH_CAP_COMMANDS PUBLIC_TWITCH_CAP_ALL PUBLIC_TWITCH_CAP_CHATROOM To clear the capability requests, simply pass 'undefined' or an empty string as a single argument. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- twitch_get_property(twitch_object, property_name) If your game has been granted the "tags" capability, it can receive special user tags marked with the "@" symbol (More info: "https://dev.twitch.tv/docs/irc#twitch-irc-capability-tags") When broken into a twitch_object, we can get this properties with this function. Some properties may have "escaped" strings (including "display-name", so you may need to unescape them. More info here: "https://ircv3.net/specs/core/message-tags-3.2.html" I've suppled the function "twitch_unescape_string" to sort that out. Simply pass the initial string as the argument, and it will return the unescaped string. argument0 is a twitch_object (usually received via the twitch_callback_script) argument1 is the string name of the property you want to find. RETURNS {string} or {undefined} - If the property exists, the value is returned as a STRING, otherwise, 'undefined' is returned. If the tag exists but has no value or an empty value assigned, the empty string "" is returned Note that I've made the tags themselves case-insensitive (argument1), though the value returned will have it's capitalisation preserved. If case-insensitive tag names causes a problem down the line, I'll make it case sensitive again, though all Twitch tags appear to be lower case anyway. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- twitch_enable_debug_output(enable) Used to enable or disable Twitch debug messages which can show useful information about the current statuses. By default, this is enabled. This script should be used BEFORE twitch_connect(); ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- twitch_enable_auto_reconnect(enable) Used to enable or disable automatic reconnection if connection was lost after initial establishment (e.g, no server response for 12 minutes, internet connection lost) By default, this is enabled. This script should be used BEFORE twitch_connect(); ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- twitch_parse_string(string) A mostly internal function, but this parses a string following the standard IRC command format into a "twitch_object". You'll likely never need to use this, but it's there if you need it ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- twitch_set_non_blocking_connect_callback(script_index) ADVANCED: If you decide to enable non-blocking sockets ("network_set_config(network_config_use_non_blocking_socket, true)") You'll find that the twitch_connect function performs slighlty differently. As connection cannot be immediately successful (connection is effectively async in this mode), the script may return true regardless of whether the final connection will be a success. In order to counteract this, you should specify a non-blocking connect callback script with this function. When connection is confirmed or denied, the callback script will be called and passed one argument - it's value will be 'true' if connection was successful, and 'false' if it failed You can then use this information to handle the situation as needed. If you have no idea what a non-blocking socket is, you probably don't need to worry about it. I just felt it was important this extension worked in either mode. This script should be used BEFORE twitch_connect(); [You MUST specify a non-blocking connection callback on HTML5 to check if connection was successful, otherwise twitch_connect will refuse as ALL connections are non-blocking. This is simply a safety feature to make sure you program your app in a safe way. The callback will also happen if the WebSocket closes for whatever reason, even if connection was already successful. Please take this into account] ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- twitch_unescape_string(string) Returns an unescaped string following these rules: "https://ircv3.net/specs/core/message-tags-3.2.html" If the input value is NOT a string, it returns the original value. Used primarily for certain tags included in twitch_objects (e.g, "display-name") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SOLUTIONS TO SOME POSSIBLE ISSUES While I haven't yet seen these issues yet, is probably preparing for the worst: Twitch times out too soon? - Increase the value of macro "__TWITCH_TIMEOUT" in "twitch_connect" to increase the maximum timeout period. When queueing lots of commands or messages in one step, some are missed? - You have two options here. Instead of using twitch_queue_command, you can use twitch_send_command. Note that this will send the command immediately, while queued commands are batched and sent at the end of every step. You can also change the value of macro "__TWITCH_MAX_COMMANDS_PER_PAYLOAD" in "twitch_connect" to some small positive number (5 should be pretty good). This changes the maximum number of batched commands, reducing risk of an oversized payload. When using HTML5, twitch_connect will return true as long as the socket is created. HOWEVER! WebSocket connections are non-blocking - this means you MUST have a non-blocking callback specified in order to check if the connection was successful [see "twitch_set_non_blocking_connect_callback"]