import os
import sys
import platform
import ac
PATH_APP = os.path.dirname(__file__)
path_stdlib=''
if platform.architecture()[0] == '64bit':
path_stdlib = os.path.join(PATH_APP, 'stdlib64')
else:
path_stdlib = os.path.join(PATH_APP, 'stdlib')
sys.path.insert(0, path_stdlib)
sys.path.insert(0, os.path.join(PATH_APP, 'third_party'))
os.environ['PATH'] = os.environ['PATH'] + ';.'
import math
import urllib.request
import json
import time
from threading import Thread
import socket
import re
import configparser
import requests
configfile = os.path.join(PATH_APP, 'twitch.ini')
config = configparser.ConfigParser()
config.read(configfile)
HOST = config['MAIN']['host']
PORT = int(config['MAIN']['port'])
NICK = config['MAIN']['nick']
PASS = config['MAIN']['pass']
CHAN = config['MAIN']['chan']
ac.initFont(0, 'Consolas', 0, 0)
BG_OPACITY = float(config['WINDOW']['opacity'])
WINDOW_WIDTH = int(config['WINDOW']['width'])
LINE_COUNT = int(config['WINDOW']['line_count'])
FONT_SIZE = int(config['WINDOW']['font_size'])
SYMBOL_COUNT = math.floor((WINDOW_WIDTH-45)/0.55/FONT_SIZE) #Consolas has ratio of 0.55
MESSAGE_HEIGHT = FONT_SIZE + 2
WINDOW_HEIGHT = MESSAGE_HEIGHT * (LINE_COUNT) + 75 + math.floor(MESSAGE_HEIGHT / 2)
CHAT_MSG=re.compile(r"^:\w+!\w+@\w+\.tmi\.twitch\.tv PRIVMSG #\w+ :")
LOGGUED_MSG=re.compile(r"^:.*?001.*?:Welcome.*")
LOGGUEDFAIL_MSG=re.compile(r"^:.*?Login authentication failed")
APP_NAME = 'Twitch chat'
messageLabel = []
messageList = ['Welcome to the twitch app','you will see the messages from defined IRC channel','you can scroll with the arrows on the right','you can answer using the text box','Have a fun stream']
while len(messageList) < LINE_COUNT:
messageList.append('')
colors = (
(255, 0, 0),
(255, 128, 0),
(255, 255, 0),
(128, 255, 0),
(0, 255, 0),
(0, 255, 128),
(0, 255, 255),
(0, 128, 255),
(0, 0, 255),
(128, 0, 255),
(255, 0, 255),
(255, 0, 128),
(128, 128, 128),
(255, 255, 255),
)
linkColorNick = [('',0)]
curr_color = 0
offsetMessage = 0
elsaped_time = 0
viewers = 'offline'
logged = 0
call = 0
running = True
fetching = False
def acMain(ac_version):
global appWindow,messageLabel,messageList,s,next,prev,end
appWindow=ac.newApp(APP_NAME)
ac.setSize(appWindow,WINDOW_WIDTH,WINDOW_HEIGHT)
ac.drawBorder(appWindow,0)
ac.setBackgroundOpacity(appWindow,BG_OPACITY)
s = socket.socket()
s.connect((HOST, PORT))
s.send("PASS {}\r\n".format(PASS).encode("utf-8"))
s.send("NICK {}\r\n".format(NICK).encode("utf-8"))
s.send("JOIN {}\r\n".format(CHAN).encode("utf-8"))
s.setblocking(False)
for i in range(0,LINE_COUNT):
messageLabel.append(ac.addLabel(appWindow,messageList[i]))
ac.setPosition(messageLabel[i],15,(i*MESSAGE_HEIGHT)+ 30 + math.floor(MESSAGE_HEIGHT / 2))
ac.setFontSize(messageLabel[i],FONT_SIZE)
ac.setCustomFont(messageLabel[i], 'Consolas', 0, 0)
input_text = ac.addTextInput(appWindow,"")
ac.setPosition(input_text,15,(MESSAGE_HEIGHT * (LINE_COUNT + 1)) + 30)
ac.setSize(input_text,WINDOW_WIDTH-46,20)
ac.addOnValidateListener(input_text,onSendMessage)
prevButton = ac.addButton(appWindow,'ᐃ')
ac.setSize(prevButton,20,20)
ac.setFontSize(prevButton,12)
ac.setPosition(prevButton,WINDOW_WIDTH-30,30)
ac.addOnClickedListener(prevButton,onClickPrev)
nextButton = ac.addButton(appWindow,'ᐁ')
ac.setSize(nextButton,20,20)
ac.setFontSize(nextButton,12)
ac.setPosition(nextButton,WINDOW_WIDTH-30,WINDOW_HEIGHT-55)
ac.addOnClickedListener(nextButton,onClickNext)
endButton = ac.addButton(appWindow,'ꓪ')
ac.setSize(endButton,20,20)
ac.setFontSize(endButton,12)
ac.setPosition(endButton,WINDOW_WIDTH-30,WINDOW_HEIGHT-35)
ac.addOnClickedListener(endButton,onClickEnd)
displayRefresh()
ac.console('Twitch app init done')
return APP_NAME
def onClickPrev(v1,v2):
global offsetMessage,messageList
if (len(messageList) > LINE_COUNT) and (offsetMessage < (len(messageList)-LINE_COUNT)):
offsetMessage += 1
displayRefresh()
def onClickNext(v1,v2):
global offsetMessage,messageList
if offsetMessage > 0:
offsetMessage -= 1
displayRefresh()
def onClickEnd(v1,v2):
global offsetMessage
offsetMessage = 0
displayRefresh()
def onSendMessage(message):
global s,messageList
s.send("PRIVMSG {} :{}\r\n".format(CHAN, message).encode("utf-8"))
colorIndex = getUsernameColor(NICK)
messageList.append((NICK+": "+message,colorIndex))
displayRefresh()
def splitMessage(mess,user):
if mess.strip() == "":
return []
mess = user + ': ' + mess
splitted = []
length_sup = True
rest_mess = mess
while length_sup == True:
if len(rest_mess) > SYMBOL_COUNT:
split_mess = rest_mess[0:SYMBOL_COUNT]
rest_mess = rest_mess[SYMBOL_COUNT:]
splitted.append(split_mess)
else:
if rest_mess.strip() != "":
splitted.append(rest_mess)
length_sup = False
return splitted
def rangeColor(value):
OldRange = (255 - 0)
NewRange = (1 - 0)
NewValue = (((value - 0) * NewRange) / OldRange) + 0
return NewValue
def displayRefresh():
global messageList,messageLabel,offsetMessage
lenList = len(messageList)
for i in range(0,LINE_COUNT):
curr_mess = messageList[i+(lenList-LINE_COUNT)-offsetMessage]
if isinstance(curr_mess, tuple):
message = curr_mess[0]
ColorIndex = curr_mess[1]
ac.setText(messageLabel[i], "{}".format(message))
R = rangeColor(colors[ColorIndex][0])
G = rangeColor(colors[ColorIndex][1])
B = rangeColor(colors[ColorIndex][2])
ac.setFontColor(messageLabel[i],R,G,B,1)
else:
ac.setText(messageLabel[i], "{}".format(curr_mess))
def getUsernameColor(nick):
global curr_color,linkColorNick
color = None
for i in linkColorNick:
if i[0] == nick:
color = i[1]
if color == None:
linkColorNick.append((nick,curr_color))
color = curr_color
curr_color += 1
if curr_color > (len(colors)-1):
curr_color = 0
return color
def getActualFollow():
global viewers,CHAN,PASS,fetching
fetching = True
try:
headers = {'Accept': 'application/vnd.twitchtv.v3+json'}
r = requests.get('https://api.twitch.tv/kraken/streams/'+CHAN[1:]+'?oauth_token='+PASS[6:], headers=headers)
jsonData = r.json()
ac.console('json {}'.format(jsonData))
if jsonData['stream'] != None:
viewers = str(jsonData['stream']['viewers'])
else:
viewers = 'offline'
except:
pass
fetching = False
def acUpdate(deltaT):
global messageList,messageLabel,s,appWindow,elsaped_time,viewers,CHAN,logged
elsaped_time += deltaT
if elsaped_time > 5 and logged == 1:
if fetching == False:
Thread(target=getActualFollow).start()
ac.setTitle(appWindow,'Twitch chat {} : {}'.format(CHAN,viewers))
elsaped_time = 0
ac.setBackgroundOpacity(appWindow,BG_OPACITY)
try:
response = s.recv(512).decode("utf-8")
if logged == 0:
res = re.search(LOGGUED_MSG, response)
if res != None:
logged = 1
messageList.append('logged in success')
displayRefresh()
res = re.search(LOGGUEDFAIL_MSG, response)
if res != None:
logged = 1
messageList.append('login fail please check your credential information')
displayRefresh()
else:
if response == "PING :tmi.twitch.tv\r\n":
s.send("PONG :tmi.twitch.tv\r\n".encode("utf-8"))
elif response != None:
result = re.search(CHAT_MSG, response)
if result != None:
result = result.group(0)
username = re.search(r"\w+", response).group(0) # return the entire match
colorIndex = getUsernameColor(username)
message = CHAT_MSG.sub("", response)
message_ready = splitMessage(message,username)
for i in message_ready:
messageList.append((i,colorIndex))
displayRefresh()
except:
pass
def acShutdown():
global s,running
running = False
s.close()