Created
October 7, 2020 14:53
-
-
Save ellisonleao/3876b12c6bcd234da4cdd608013a5924 to your computer and use it in GitHub Desktop.
twitch.lua
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- twitch module | |
| local ws = require("http.websocket") | |
| local utils = require("utils") | |
| local M = {} | |
| local twitch_url = "wss://irc-ws.chat.twitch.tv:443" | |
| local message_types = { | |
| PING = "PING", | |
| PRIVMSG = "PRIVMSG", | |
| ACK = "ACK", | |
| JOIN = "JOIN", | |
| USERSTATE = "USERSTATE", | |
| } | |
| local pattern = ":.*tmi.twitch.tv%s*(%u+)%s*#(%a+)(.*)" | |
| -- parses twitch websocket msg and return a table with contents | |
| -- @param data the websocket received string data | |
| -- @return parsed table message | |
| local function parse_msg(data) | |
| if utils.startswith(data, "PING") then | |
| return {type = message_types.PING, raw = data} | |
| end | |
| local splitted = utils.split(data, ";") | |
| if #splitted == 1 then | |
| return {type = "", raw = data} | |
| end | |
| local fields = {raw = data} | |
| for _, item in pairs(splitted) do | |
| local key, val = unpack(utils.split(item, "=")) | |
| fields[key] = val | |
| end | |
| local last = fields["user-type"] | |
| local _, _, msg_type, chan, text = string.find(last, pattern) | |
| fields["type"] = msg_type | |
| fields["channel"] = chan | |
| if msg_type == message_types.PRIVMSG and text ~= nil then | |
| text, _ = string.gsub(utils.strip(text), ":", "") | |
| fields["text"] = text | |
| end | |
| return fields | |
| end | |
| -- on PRIVMSG handler | |
| -- @param data parsed privmsg data table | |
| local function on_privmsg(data) | |
| print(data["display-name"] .. ": " .. data["text"]) | |
| end | |
| -- Creates a new websocket connection to twitch chat wss url. | |
| -- Returns the websocket connection | |
| -- @param channel The channel you want to connect to | |
| -- @param token Your Oauth token format `oauth:TOKEN` | |
| -- @return websocket connection | |
| function M.new(token) | |
| local conn = ws.new_from_uri(twitch_url) | |
| assert(conn:connect()) | |
| assert(conn:send(string.format([[PASS %s ]], token))) | |
| assert(conn:send(string.format([[NICK npxbr]], token))) | |
| return conn | |
| end | |
| -- Closes the current websocket connection | |
| -- @param conn active connection | |
| function M.close(conn) | |
| assert(conn:close()) | |
| end | |
| -- send a CAP REQ request | |
| -- @param arg twitch capability. Options are membership,tags,commands | |
| function M.request_capability(conn, arg) | |
| assert(conn:send([[CAP REQ :twitch.tv/]] .. arg)) | |
| end | |
| -- Helper to request all current twitch capabilities | |
| -- @param conn websocket connection | |
| function M.request_all_capabilities(conn) | |
| M.request_capability(conn, "membership") | |
| M.request_capability(conn, "tags") | |
| M.request_capability(conn, "commands") | |
| end | |
| -- join channel request | |
| -- @param conn websocket connection | |
| -- @channel twitch channel (without #) | |
| function M.join_channel(conn, channel) | |
| assert(conn:send([[JOIN #]] .. channel)) | |
| end | |
| -- main function, initializes connection, requests all capabilities and joins channel | |
| -- @param channel twitch channel | |
| -- @tokean Twitch OAUTH token string. Format: "oauth:TOKEN" | |
| function M.init(channel, token) | |
| local conn = M.new(token) | |
| M.request_all_capabilities(conn) | |
| M.join_channel(conn, channel) | |
| print("connected to " .. channel) | |
| -- main connection loop | |
| while true do | |
| for data, _ in conn:each() do | |
| local parsed = parse_msg(data) | |
| -- handle PRIVMSGs | |
| if parsed.type == message_types.PRIVMSG then | |
| on_privmsg(parsed) | |
| end | |
| -- need to send PONG if PING message is received to keep connection alive | |
| if parsed.type == message_types.PING then | |
| M.pong(conn) | |
| end | |
| end | |
| end | |
| -- TODO(ellisonleao) check for keyboard events / kill signals to close connection | |
| M.close() | |
| end | |
| function M.create_command() | |
| vim.cmd([[command! TwitchChat lua require("twitch").init()]]) | |
| end | |
| -- sends pong message | |
| -- @param conn websocket connection | |
| function M.pong(conn) | |
| assert(conn:send_pong()) | |
| end | |
| return M |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment