Skip to content

Instantly share code, notes, and snippets.

@ellisonleao
Created October 7, 2020 14:53
Show Gist options
  • Select an option

  • Save ellisonleao/3876b12c6bcd234da4cdd608013a5924 to your computer and use it in GitHub Desktop.

Select an option

Save ellisonleao/3876b12c6bcd234da4cdd608013a5924 to your computer and use it in GitHub Desktop.
twitch.lua
-- 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