You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

249 lines
6.8 KiB

extends IrcClientSecure
class_name TwiCIL
signal raw_response_received(response)
signal user_appeared(user)
signal user_disappeared(user)
signal message_received(sender, text, emotes)
signal emote_received(user, emote_reference)
signal texture_received(texture)
enum IRCCommands {PING, PONG, PRIVMSG, JOIN, PART, NAMES}
const TWITCH_IRC_CHAT_HOST = 'wss://irc-ws.chat.twitch.tv'
const TWITCH_IRC_CHAT_PORT = 443
#const CONNECT_WAIT_TIMEOUT = 1
#const COMMAND_WAIT_TIMEOUT = 1.5
onready var tools = HelperTools.new()
onready var commands = InteractiveCommands.new()
onready var chat_list = ChatList.new()
var twitch_emotes_cache: TwitchEmotesCache
var bttv_emotes_cache: BttvEmotesCache
var ffz_emotes_cache: FfzEmotesCache
var twitch_api_wrapper: TwitchApiWrapper
var irc_commands = {
IRCCommands.PING: 'PING',
IRCCommands.PONG: 'PONG',
IRCCommands.PRIVMSG: 'PRIVMSG',
IRCCommands.JOIN: 'JOIN',
IRCCommands.PART: 'PART',
IRCCommands.NAMES: '/NAMES'
}
var curr_channel = ""
#{
# "emote_id": ["user_name#1", "user_name#2", ...]
# ...
#}
var user_emotes_queue := Dictionary()
# Public methods
func connect_to_twitch_chat():
.connect_to_host(TWITCH_IRC_CHAT_HOST, TWITCH_IRC_CHAT_PORT)
func connect_to_channel(channel, client_id, password, nickname, realname=''):
_connect_to(
channel,
nickname,
nickname if realname == '' else realname,
password,
client_id
)
bttv_emotes_cache.init_emotes(curr_channel)
twitch_api_wrapper.set_credentials(client_id, password)
func _connect_to(channel, nickname, realname, password, client_id):
.send_command('PASS %s' % password)
.send_command('NICK ' + nickname)
.send_command(str('USER ', client_id, ' ', _host, ' bla:', realname))
.send_command('JOIN #' + channel)
.send_command("CAP REQ :twitch.tv/tags twitch.tv/commands twitch.tv/membership")
curr_channel = channel
func send_message(text):
.send_command(str('PRIVMSG #', curr_channel, ' :', text))
func send_whisper(recepient, text):
send_message(str('/w ', recepient, ' ', text))
func request_twitch_emote(user_name: String, id: int) -> void:
if not user_emotes_queue.has(id):
user_emotes_queue[id] = []
user_emotes_queue[id].append(user_name)
twitch_emotes_cache.get_emote(id)
func request_bttv_emote(user_name: String, code: String) -> void:
var id: String = bttv_emotes_cache.available_emotes.get(code)
if not user_emotes_queue.has(id):
user_emotes_queue[id] = []
user_emotes_queue[id].append(user_name)
bttv_emotes_cache.get_emote(code)
func request_ffz_emote(user_name: String, code: String) -> void:
var id: String = ffz_emotes_cache.available_emotes.get(code, {}).get('id', '')
if not user_emotes_queue.has(id):
user_emotes_queue[id] = []
user_emotes_queue[id].append(user_name)
ffz_emotes_cache.get_emote(code)
func request_emote_from(emotes: Array, user_name: String, index: int) -> void:
if emotes.empty():
return
var emote = emotes[index]
if emote.get('type') == TwitchMessage.EmoteType.TWITCH:
var emote_id := int(emote.get('id'))
request_twitch_emote(user_name, emote_id)
elif emote.get('type') == TwitchMessage.EmoteType.BTTV:
var emote_code := emote.get('code') as String
request_bttv_emote(user_name, emote_code)
elif emote.get('type') == TwitchMessage.EmoteType.FFZ:
var emote_code := emote.get('code') as String
request_ffz_emote(user_name, emote_code)
# Private methods
func __init_emotes_caches() -> void:
twitch_emotes_cache = TwitchEmotesCache.new()
add_child(twitch_emotes_cache)
bttv_emotes_cache = BttvEmotesCache.new()
add_child(bttv_emotes_cache)
ffz_emotes_cache = FfzEmotesCache.new()
add_child(ffz_emotes_cache)
func __init_twitch_api() -> void:
twitch_api_wrapper = TwitchApiWrapper.new(http_request_queue, '')
func __connect_signals():
connect("message_received", commands, "_on_message_received")
connect("response_received", self, "_on_response_received")
connect("http_response_received", self, "_on_http_response_received")
twitch_emotes_cache.connect("emote_retrieved", self, "_on_emote_retrieved")
bttv_emotes_cache.connect("emote_retrieved", self, "_on_emote_retrieved")
ffz_emotes_cache.connect("emote_retrieved", self, "_on_emote_retrieved")
twitch_api_wrapper.connect("api_user_info", self, "_on_twitch_api_api_user_info")
func __parse(string: String) -> TwitchIrcServerMessage:
var args = []
var twitch_prefix = ''
var prefix = ''
var trailing = []
var command
if string == null:
return TwitchIrcServerMessage.new('', '','', [])
if string.substr(0, 1) == '@':
var temp = tools.split_string(string.substr(1, string.length() - 1), ' ', 1)
twitch_prefix = temp[0]
string = temp[1]
if string.substr(0, 1) == ':':
var temp = tools.split_string(string.substr(1, string.length() - 1), ' ', 1)
prefix = temp[0]
string = temp[1]
if string.find(' :') != -1:
var temp = tools.split_string(string, ' :', 1)
string = temp[0]
trailing = temp[1]
args = tools.split_string(string, [' ', '\t', '\n'])
args.append(trailing)
else:
args = tools.split_string(string, [' ', '\t', '\n'])
command = args[0]
args.pop_front()
return TwitchIrcServerMessage.new(twitch_prefix, prefix, command, args)
# Hooks
func _ready():
__init_twitch_api()
__init_emotes_caches()
__connect_signals()
# Events
func _on_response_received(response):
emit_signal("raw_response_received", response)
for single_response in response.split('\n', false):
single_response = __parse(single_response.strip_edges(false))
# Ping-Pong with server to let it know we're alive
if single_response.command == irc_commands[IRCCommands.PING]:
.send_command(str(irc_commands[IRCCommands.PONG], ' ', single_response.params[0]))
# Message received
elif single_response.command == irc_commands[IRCCommands.PRIVMSG]:
var twitch_message: TwitchMessage = TwitchMessage.new(
single_response,
bttv_emotes_cache.available_emotes,
ffz_emotes_cache.available_emotes
)
emit_signal(
"message_received",
twitch_message.chat_message.name,
twitch_message.chat_message.text,
twitch_message.emotes
)
elif single_response.command == irc_commands[IRCCommands.JOIN]:
var user_name = MessageWrapper.get_sender_name(single_response)
chat_list.add_user(user_name)
._log(str(user_name, " has joined chat"))
emit_signal("user_appeared", user_name)
elif single_response.command == irc_commands[IRCCommands.PART]:
var user_name = MessageWrapper.get_sender_name(single_response)
chat_list.remove_user(user_name)
._log(str(user_name, " has left chat"))
emit_signal("user_disappeared", user_name)
func _on_emote_retrieved(emote_reference: Reference) -> void:
var emote_id: String = emote_reference.id
var user: String = (user_emotes_queue.get(emote_id, []) as Array).pop_front()
emit_signal("emote_received", user, emote_reference)
func _on_twitch_api_api_user_info(data):
var user_id := str(data.get('data', [{}])[0].get('id', 0))
ffz_emotes_cache.init_emotes(user_id)