scripted upload to gitlab

This commit is contained in:
MrARM
2020-06-23 23:17:05 -05:00
commit 9108b97032
18 changed files with 9980 additions and 0 deletions

104
.gitignore vendored Normal file
View File

@@ -0,0 +1,104 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
*.log
.idea/

2
conn_settings.txt Normal file
View File

@@ -0,0 +1,2 @@
BEARER_TOKEN=hLdtXH8baCeRCtoW8ARUB0CRcfSeyOH8eocOyqQ4nmuZtXwKw2zVZcb1rwtoiMDP","accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjIyNzU1MDU4LCJ1c2VybmFtZSI6IlJpY2hhcmQxNTAiLCJhdmF0YXJVcmwiOiJzMzovL2h5cGVzcGFjZS1xdWl6L2RlZmF1bHRfYXZhdGFycy9VbnRpdGxlZC0xXzAwMDFfYmx1ZS5wbmciLCJ0b2tlbiI6ImhMZHRYSCIsInJvbGVzIjpbXSwiY2xpZW50IjoiIiwiZ3Vlc3RJZCI6bnVsbCwidiI6MSwiaWF0IjoxNTM1NDE4MjQ1LCJleHAiOjE1NDMxOTQyNDUsImlzcyI6Imh5cGVxdWl6LzEifQ.upfa3v5QCW3sl2bSt5vMyroDYuwIdExds_OiiSfCT_A
USER_ID=21149316

357
hq.ui Normal file
View File

@@ -0,0 +1,357 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>HQApp</class>
<widget class="QWidget" name="HQApp">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1298</width>
<height>440</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">QWidget#HQApp {
background-color: #36399A;
}
QLabel { color: white; }</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>300</height>
</size>
</property>
<property name="pixmap">
<pixmap>images/hqlogo.jpg</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>NEXT GAME</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>$5,000 Prize</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>2PM CDT</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1" rowspan="6">
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QWidget" name="GooglePanel" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">QWidget#GooglePanel {
background-color: white;
border-radius: 10px;
}</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="1" column="0">
<layout class="QGridLayout" name="gridLayout_4">
<item row="1" column="0">
<widget class="QProgressBar" name="progressBar_3">
<property name="value">
<number>24</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="format">
<string>Answer B</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QProgressBar" name="progressBar_2">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>63</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="format">
<string>Answer A</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QProgressBar" name="progressBar_4">
<property name="value">
<number>12</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="format">
<string>Answer C</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="maximumSize">
<size>
<width>300</width>
<height>100</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>images/google.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QWidget" name="WebpagePanel" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">QWidget#WebpagePanel {
background-color: white;
border-radius: 10px;
}
QLabel { color: black; }</string>
</property>
<layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="maximumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>images/webpage.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignJustify|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_8">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>Website Search</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_6">
<item row="1" column="0">
<widget class="QProgressBar" name="progressBar_5">
<property name="value">
<number>24</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="format">
<string>Answer B</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QProgressBar" name="progressBar_6">
<property name="value">
<number>24</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="format">
<string>Answer A</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QProgressBar" name="progressBar_7">
<property name="value">
<number>52</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="format">
<string>Answer C</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QWidget" name="widget_3" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">background-color: white;
border-radius: 10px;
color: white;</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: black;</string>
</property>
<property name="text">
<string>[Q1] What is a question?</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

365
hq_gui.py Normal file
View File

@@ -0,0 +1,365 @@
import asyncio
from question import QuestionHandler
from PyQt5.QtGui import QFont, QPixmap, QPalette, QColor, QFontMetrics
from PyQt5.QtWidgets import QWidget, QPushButton, QLabel, QProgressBar, QGridLayout
from PyQt5.QtWidgets import QFrame, QSpacerItem, QSizePolicy
from PyQt5.QtCore import QRect, Qt, QEasingCurve, pyqtSignal, QPropertyAnimation, QUrl
import vlc
import re
def getPropertyAnimation(self, name, duration, start, end, keyValues={}):
animation = QPropertyAnimation(self, bytes(str.encode(name)))
animation.setDuration(duration)
animation.setStartValue(start)
animation.setEndValue(end)
for k, v in keyValues.items():
animation.setKeyValueAt(k, v)
return animation
class VideoFrame(QFrame):
def resizeEvent(self, event):
super().resizeEvent(event)
print(event.size())
self.setFixedWidth((300/535) * event.size().height())
class HQApp(QWidget):
nextGameInfo = pyqtSignal([str, str])
streamLive = pyqtSignal(str)
streamStop = pyqtSignal()
getTrivia = pyqtSignal()
def __init__(self, q):
super().__init__()
self.q = q
self.googleAnswers = []
self.webpageAnswers = []
self.animations = []
self.streaming = False
self.vlc = vlc.Instance()
self.player = self.vlc.media_player_new()
self.setupUI()
self.setupSignals()
def setupUI(self):
self.resize(1300, 600)#.setFixedSize(1300, 440)
self.setObjectName('HQApp')
self.setStyleSheet('QWidget#HQApp { background-color: #36399A; } QLabel { color: white; }')
layout = QGridLayout(self)
layout.setContentsMargins(9, 9, 200, 9)
self.hqLogo = QLabel(self)
self.hqLogo.setPixmap(QPixmap('images/hqlogo.jpg'))
self.hqLogo.setMaximumSize(300, 300)
self.hqLogo.setScaledContents(True)
layout.addWidget(self.hqLogo, 1, 0, 1, 1)
self.showStatus = QLabel(self)
self.showStatus.setText('NEXT GAME')
self.showStatus.hide()
self.showStatus.setAlignment(Qt.AlignCenter)
layout.addWidget(self.showStatus, 2, 0, 1, 1)
font = QFont()
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.showTime = QLabel(self)
self.showTime.setText('2PM CDT')
self.showTime.hide()
self.showTime.setAlignment(Qt.AlignCenter)
self.showTime.setFont(font)
layout.addWidget(self.showTime, 3, 0, 1, 1)
self.showPrize = QLabel(self)
self.showPrize.setText('$5,000 Prize')
self.showPrize.hide()
self.showPrize.setAlignment(Qt.AlignCenter)
self.showPrize.setFont(font)
layout.addWidget(self.showPrize, 4, 0, 1, 1)
self.videoFrame = VideoFrame()
self.videoFrame.resize(300, 420)
p = self.videoFrame.palette()
p.setColor(QPalette.Window, QColor(0, 0, 0))
self.videoFrame.setPalette(p)
self.videoFrame.setAutoFillBackground(True)
self.videoFrame.hide()
layout.addWidget(self.videoFrame, 0, 0, 6, 1)
topSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
bottomSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
layout.addItem(topSpacer, 0, 0, 1, 1)
layout.addItem(bottomSpacer, 5, 0, 1, 1)
rlayout = QGridLayout()
layout.addLayout(rlayout, 0, 1, 6, 1)
topSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
bottomSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
rlayout.addItem(topSpacer, 0, 0, 1, 1)
rlayout.addItem(bottomSpacer, 3, 0, 1, 1)
self.googleBG = QWidget(self)
self.googleBG.setStyleSheet('QWidget#GooglePanel { background-color: white; border-radius: 10px; }')
#sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
#sizePolicy.setHorizontalStretch(1)
#sizePolicy.setVerticalStretch(1)
#sizePolicy.setHeightForWidth(self.googleBG.sizePolicy().hasHeightForWidth())
#self.googleBG.setSizePolicy(sizePolicy)
self.googleBG.setObjectName('GooglePanel')
googleLayout = QGridLayout(self.googleBG)
rlayout.addWidget(self.googleBG, 2, 0, 1, 1)
self.webpageBG = QWidget(self)
self.webpageBG.setStyleSheet('QWidget#WebpagePanel { background-color: white; border-radius: 10px; }')
#sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
#sizePolicy.setHorizontalStretch(1)
#sizePolicy.setVerticalStretch(1)
#sizePolicy.setHeightForWidth(self.webpageBG.sizePolicy().hasHeightForWidth())
#self.webpageBG.setSizePolicy(sizePolicy)
self.webpageBG.setObjectName('WebpagePanel')
webpageLayout = QGridLayout(self.webpageBG)
rlayout.addWidget(self.webpageBG, 2, 1, 1, 1)
self.googleLogo = QLabel(self.googleBG)
self.googleLogo.setPixmap(QPixmap('images/google.png'))
self.googleLogo.setMaximumSize(300, 100)
self.googleLogo.setScaledContents(True)
googleLayout.addWidget(self.googleLogo, 0, 1, 1, 1)
leftSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
rightSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
googleLayout.addItem(leftSpacer, 0, 0, 1, 1)
googleLayout.addItem(rightSpacer, 0, 2, 1, 1)
self.webpageLogo = QLabel(self.webpageBG)
self.webpageLogo.setPixmap(QPixmap('images/webpage.png'))
self.webpageLogo.setMaximumSize(100, 100)
self.webpageLogo.setScaledContents(True)
self.webpageText = QLabel('Website Search', self.webpageBG)
font = QFont()
font.setPointSize(13)
self.webpageText.setFont(font)
self.webpageText.setStyleSheet('color: black;')
webpageLayout.addWidget(self.webpageLogo, 0, 1, 1, 1)
webpageLayout.addWidget(self.webpageText, 0, 2, 1, 1)
leftSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
rightSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
webpageLayout.addItem(leftSpacer, 0, 0, 1, 1)
webpageLayout.addItem(rightSpacer, 0, 3, 1, 1)
#self.galWidget = QWidget(self)
#self.galWidget.setGeometry(QRect(360, 300, 341, 171))
self.googleAnswerLayout = QGridLayout()
self.googleAnswerLayout.setContentsMargins(0, 0, 0, 0)
self.googleAnswerA = AnswerBar('A', self)
self.googleAnswerB = AnswerBar('B', self)
self.googleAnswerC = AnswerBar('C', self)
self.googleAnswers = [self.googleAnswerA, self.googleAnswerB, self.googleAnswerC]
for answer in self.googleAnswers:
answer.hide()
self.googleAnswerLayout.addWidget(self.googleAnswerA, 0, 0, 1, 1)
self.googleAnswerLayout.addWidget(self.googleAnswerB, 1, 0, 1, 1)
self.googleAnswerLayout.addWidget(self.googleAnswerC, 2, 0, 1, 1)
self.googleExplanation = QLabel(
"Google answers come straight from Google results themselves, meaning that when it works, it's often the most realiable. " \
"If Google's confident, it may be because of it's smart card / feedback system... a definite sign that it has the correct answer.\n\nBoth answer " \
"sources return the reverse order of its findings if the question has a negative term in it -- such as \"NOT\"," \
" which may be inaccurate if the Google search also considers the \"NOT\" when it's returning results. Be wary."
)
self.googleExplanation.setStyleSheet('color: black;')
self.googleExplanation.setWordWrap(True)
self.googleAnswerLayout.addWidget(self.googleExplanation, 0, 0, 3, 1)
googleLayout.addLayout(self.googleAnswerLayout, 1, 0, 1, 3)
#self.walWidget = QWidget(self)
#self.walWidget.setGeometry(QRect(750, 300, 341, 171))
self.webpageAnswerLayout = QGridLayout()
self.webpageAnswerLayout.setContentsMargins(0, 0, 0, 0)
self.webpageAnswerA = AnswerBar('A', self)
self.webpageAnswerB = AnswerBar('B', self)
self.webpageAnswerC = AnswerBar('C', self)
self.webpageAnswers = [self.webpageAnswerA, self.webpageAnswerB, self.webpageAnswerC]
for answer in self.webpageAnswers:
answer.hide()
self.webpageAnswerLayout.addWidget(self.webpageAnswerA, 0, 0, 1, 1)
self.webpageAnswerLayout.addWidget(self.webpageAnswerB, 1, 0, 1, 1)
self.webpageAnswerLayout.addWidget(self.webpageAnswerC, 2, 0, 1, 1)
self.webpageExplanation = QLabel(
"Webpage results is the plain text of the sites in the Google results split into keywords and compared " \
"against the available answers. This can be helpful for questions that are not so straightforward, " \
"since websites contain the complete text of what they have to say, rather than Google's small previews and compact card information. " \
"\n\nUsually if Google " \
"and the website search have eliminated one answer but can't decide between the other two, whichever one is " \
"chosen by both Google and the website search is correct."
)
self.webpageExplanation.setStyleSheet('color: black;')
self.webpageExplanation.setWordWrap(True)
self.webpageAnswerLayout.addWidget(self.webpageExplanation, 0, 0, 3, 1)
webpageLayout.addLayout(self.webpageAnswerLayout, 1, 0, 1, 4)
self.questionBG = QWidget(self)
self.questionBG.setStyleSheet('background-color: white; border-radius: 10px; color: black;')
#sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
#sizePolicy.setHorizontalStretch(1)
#sizePolicy.setVerticalStretch(1)
#sizePolicy.setHeightForWidth(self.googleBG.sizePolicy().hasHeightForWidth())
#self.googleBG.setSizePolicy(sizePolicy)
questionLayout = QGridLayout(self.questionBG)
rlayout.addWidget(self.questionBG, 1, 0, 1, 2)
font = QFont()
font.setPointSize(20)
font.setBold(True)
font.setWeight(75)
self.question = QLabel(self.questionBG)
self.question.setFont(font)
self.question.setAlignment(Qt.AlignCenter)
self.question.setText('HQ Trivia')
questionLayout.addWidget(self.question, 0, 0, 1, 1)
self.button = QPushButton('Get Trivia', self)
layout.addWidget(self.button, 6, 0, 1, 1)
#self.videoFrame.show()
self.show()
for answer in self.googleAnswers + self.webpageAnswers:
answer.setValue(0)
def setupSignals(self):
self.nextGameInfo.connect(self.setNextGame)
self.streamLive.connect(self.startStream)
self.streamStop.connect(self.stopStream)
#self.button.clicked.connect(self.resetCount)
self.q.searching.connect(self.newSearch)
self.q.googleResults.connect(self.setGoogleResults)
self.q.pageResults.connect(self.setWebpageResults)
def setNextGame(self, time, prize):
self.showTime.setText(time)
self.showPrize.setText(prize + ' Prize')
self.showStatus.show()
self.showTime.show()
self.showPrize.show()
def newSearch(self, qn, newQuestion, answers, formattedAnswers):
self.animations.clear()
questionText = '[Q{}] {}'.format(qn, newQuestion)
textWidth = self.questionBG.width() - 10
textSize = 20
while textWidth >= self.questionBG.width() - 10:
font = QFont()
font.setPointSize(textSize)
font.setBold(True)
font.setWeight(75)
textWidth = QFontMetrics(font).width(questionText)
textSize -= 1
self.question.setFont(font)
self.question.setText(questionText)
self.answers = {answers[0]: formattedAnswers[0], answers[1]: formattedAnswers[1], answers[2]: formattedAnswers[2]}
self.googleExplanation.hide()
self.webpageExplanation.hide()
for answer in self.googleAnswers + self.webpageAnswers:
if answer.letter == 'A': answer.setAnswer(answers[0])
elif answer.letter == 'B': answer.setAnswer(answers[1])
elif answer.letter == 'C': answer.setAnswer(answers[2])
answer.show()
if answer.maximum() == 0:
answer.setValue(0)
answer.setMaximum(1)
else:
animation = getPropertyAnimation(answer, 'value', 1000, answer.value(), 0)
animation.setEasingCurve(QEasingCurve.OutQuad)
self.animations.append(animation)
animation.start()
def setGoogleResults(self, results, reverse):
if any([type(v) == dict for v in results.values()]):
formattedResults = {}
for a, d in results.items():
formattedResults[a] = sum(d.values())
results = formattedResults
total = sum(results.values()) * 1000
for answer in self.googleAnswers:
answer.setMaximum(total)
for orgAnswer, formatAnswer in self.answers.items():
if answer.text() == orgAnswer:
for rname, count in results.items():
if re.sub('[^a-zA-Z0-9 ]', '', rname).lower() == re.sub('[^a-zA-Z0-9 ]', '', orgAnswer).lower():
if reverse:
if count == max(results.values()):
count = min(results.values())
elif count == min(results.values()):
count = max(results.values())
animation = getPropertyAnimation(answer, 'value', 1000, 0, count * 1000)
animation.setEasingCurve(QEasingCurve.OutQuad)
self.animations.append(animation)
animation.start()
def setWebpageResults(self, results, reverse):
total = sum(results.values()) * 1000
for answer in self.webpageAnswers:
answer.setMaximum(total)
for orgAnswer, formatAnswer in self.answers.items():
if answer.text() == orgAnswer:
for rname, count in results.items():
if re.sub('[^a-zA-Z0-9 ]', '', rname).lower() == re.sub('[^a-zA-Z0-9 ]', '', orgAnswer).lower():
if reverse:
if count == max(results.values()):
count = min(results.values())
elif count == min(results.values()):
count = max(results.values())
animation = getPropertyAnimation(answer, 'value', 1000, 0, count * 1000)
animation.setEasingCurve(QEasingCurve.OutQuad)
self.animations.append(animation)
animation.start()
def startStream(self, url):
if self.streaming:
return
self.media = self.vlc.media_new(url)
self.player.set_media(self.media)
self.player.set_hwnd(self.videoFrame.winId())
self.streaming = True
self.player.play()
self.videoFrame.show()
def stopStream(self):
self.streaming = False
self.player.stop()
self.videoFrame.hide()
class AnswerBar(QProgressBar):
def __init__(self, letter, parent):
super().__init__(parent)
#self.setAnswer('Answer {}'.format(letter.upper()))
self.letter = letter
self.setAnswer('')
self.setAlignment(Qt.AlignCenter)
self.setCount(0, 100)
self.setStyleSheet("QProgressBar { border: 2px solid grey; border-radius: 5px; }\n"
"QProgressBar::chunk { background-color: #50C994; width: 1px; }")
def setAnswer(self, answer):
self.setFormat(answer)
def setCount(self, count, total):
self.setMaximum(total)
self.setValue(count)
if __name__ == '__main__':
from PyQt5.QtWidgets import QApplication
import sys
app = QApplication(sys.argv)
hq = HQApp()
hq.setupUI()
app.exec_()

110
hq_main.py Normal file
View File

@@ -0,0 +1,110 @@
import asyncio
import logging
import requests
import sys
import time
import csv
from ast import literal_eval
from datetime import datetime
from question import QuestionHandler
import networking
from PyQt5.QtWidgets import QApplication
from hq_gui import HQApp
from quamash import QEventLoop, QThreadExecutor
app = QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
# Set up logging
logging.basicConfig(filename="data.log", level=logging.INFO, filemode="w")
# Read in bearer token and user ID
with open("conn_settings.txt", "r") as conn_settings:
BEARER_TOKEN = conn_settings.readline().strip().split("=")[1]
USER_ID = conn_settings.readline().strip().split("=")[1]
print("getting")
main_url = f"https://api-quiz.hype.space/shows/now?type=hq&userId={USER_ID}"
headers = {"Authorization": f"Bearer {BEARER_TOKEN}",
"x-hq-client": "Android/1.6.1"}
HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0",
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate"}
# "x-hq-stk": "MQ==",
# "Connection": "Keep-Alive",
# "User-Agent": "okhttp/3.8.0"}
q = QuestionHandler()
hqApp = HQApp(q)
import re
import random
trivia = []
with open('trivia2.csv', 'r') as csvfile:
reader = csv.reader(csvfile)
for row in reader:
if row[1] == 'options': continue
question = re.sub(r'^\d+\. ?', '', row[0].strip())
if 'which' not in question.lower(): continue
answers = literal_eval(row[1])
if len(answers) != 3:
continue
correct = int(row[2])
trivia.append({'q': question, 'a': answers, 'c': correct})
i = 0
def getTrivia():
global i
i += 1
result = requests.get('https://opentdb.com/api.php?amount=1&type=multiple').json()['results'][0]
question = result['question'].replace('&quot;', '').replace('&#039;', "'")
answers = [result['correct_answer']] + result['incorrect_answers'][:2]
loop.create_task(q.answer_question(str(i), question, answers))
hqApp.button.clicked.connect(getTrivia)
#hqApp.streamLive.emit('rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov')
#hqApp.videoFrame.raise_()
#hqApp.button.hide()
while True:
print()
try:
app.processEvents()
response_data = loop.run_until_complete(
networking.get_json_response(main_url, timeout=1.5, headers=headers))
except Exception as e:
import traceback
print(traceback.format_exc())
print("Server response not JSON, retrying...")
loop.run_until_complete(asyncio.sleep(1))
continue
logging.info(response_data)
if "broadcast" not in response_data or response_data["broadcast"] is None:
if "error" in response_data and response_data["error"] == "Auth not valid":
raise RuntimeError("Connection settings invalid")
else:
print("Show not on.")
next_time = datetime.strptime(response_data["nextShowTime"], "%Y-%m-%dT%H:%M:%S.000Z")
now = time.time()
offset = datetime.fromtimestamp(now) - datetime.utcfromtimestamp(now)
print(f"Next show time: {(next_time + offset).strftime('%Y-%m-%d %I:%M %p')}")
print("Prize: " + response_data["nextShowPrize"])
hqApp.nextGameInfo.emit((next_time + offset).strftime('%Y-%m-%d %I:%M %p'), response_data['nextShowPrize'])
if hqApp.streaming:
hqApp.streamStop.emit()
loop.run_until_complete(asyncio.sleep(5))
else:
socket = response_data["broadcast"]["socketUrl"]
stream = response_data["broadcast"]["streamUrl"]
print(stream)
print(f"Show active, connecting to socket at {socket}")
if not hqApp.streaming:
hqApp.streamLive.emit(stream)
hqApp.videoFrame.raise_()
loop.run_until_complete(networking.websocket_handler(q, socket, headers))

BIN
images/google.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
images/hq-100.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
images/hqlogo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
images/webpage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

5
l.py Normal file
View File

@@ -0,0 +1,5 @@
import PyLivestream
s = PyLivestream.Screenshare('stream.ini', 'youtube')
print('going live on', s.sites)
s.golive()

74
networking.py Normal file
View File

@@ -0,0 +1,74 @@
import asyncio
import json
import logging
import re
import aiohttp
import question
async def fetch(url, session, timeout):
try:
async with session.get(url, timeout=timeout) as response:
return await response.text()
except asyncio.TimeoutError:
print(f"Server timeout ({timeout}) to {url}")
logging.exception(f"Server timeout ({timeout}) to {url}")
return ""
except Exception:
print(f"Server error to {url}")
logging.exception(f"Server error to {url}")
return ""
async def get_responses(urls, timeout, headers):
tasks = []
async with aiohttp.ClientSession(headers=headers) as session:
for url in urls:
task = asyncio.ensure_future(fetch(url, session, timeout))
tasks.append(task)
responses = await asyncio.gather(*tasks)
return responses
async def get_response(url, timeout, headers):
async with aiohttp.ClientSession(headers=headers) as session:
return await fetch(url, session, timeout)
async def get_json_response(url, timeout, headers):
async with aiohttp.ClientSession(headers=headers) as session:
async with session.get(url, timeout=timeout) as response:
return await response.json()
async def websocket_handler(handler, uri, headers):
async with aiohttp.ClientSession() as session:
async with session.ws_connect(uri, headers=headers, heartbeat=5) as ws:
print("Connected")
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
message = msg.data
message = re.sub(r"[\x00-\x1f\x7f-\x9f]", "", message)
message_data = json.loads(message)
logging.info(str(message_data).encode("utf-8"))
if "error" in message_data and message_data["error"] == "Auth not valid":
logging.info(message_data)
raise RuntimeError("Connection settings invalid")
elif message_data["type"] != "interaction":
logging.info(message_data)
if message_data["type"] == "question":
question_str = message_data["question"]
answers = [ans["text"] for ans in message_data["answers"] if ans["text"].strip() != ""]
print("\n" * 5)
print("Question detected.")
print(f"Question {message_data['questionNumber']} out of {message_data['questionCount']}")
print(question_str)
print(answers)
print()
print(await handler.answer_question(message_data['questionNumber'], question_str, answers))
print("Socket closed")

147
question.py Normal file
View File

@@ -0,0 +1,147 @@
import itertools
import re
import time
from collections import defaultdict
from PyQt5.QtCore import pyqtSignal, QObject
from unidecode import unidecode
import search
punctuation_to_none = str.maketrans({key: None for key in "!\"#$%&\'()*+,-.:;<=>?@[\\]^_`{|}~<7E>"})
punctuation_to_space = str.maketrans({key: " " for key in "!\"#$%&\'()*+,-.:;<=>?@[\\]^_`{|}~<7E>"})
class QuestionHandler(QObject):
searching = pyqtSignal([str, str, list, list])
googleResults = pyqtSignal([dict, bool])
pageResults = pyqtSignal([dict, bool])
async def answer_question(self, qn, question, original_answers):
print("Searching")
start = time.time()
question = unidecode(question)
answers = []
for ans in original_answers:
ans = unidecode(ans)
answers.append(ans.translate(punctuation_to_none))
#answers.append(ans.translate(punctuation_to_space))
answers = list(dict.fromkeys(answers))
print('Possible Answers: {}'.format(answers))
self.searching.emit(str(qn), question, original_answers, answers)
reverse = True
if ("NOT" in question and '"NOT"' not in question):
question = re.sub(r'\bNOT\b', ' ', question)
elif ("least" in question.lower() and "at least" not in question.lower() and '"least"' not in question.lower()):
question = re.sub(r'\bleast\b', ' most ', question)
elif ("fewest" in question and '"fewest"' not in question):
question = re.sub(r'\bfewest\b', ' most ', question)
elif ("NEVER" in question and '"NEVER"' not in question):
question = re.sub(r'\bNOT\b', ' always ', question)
elif ("NON" in question and '"NON"' not in question):
question = re.sub(r'\bNON', ' ', question)
else:
reverse = False
question_lower = question.lower().replace('"', '').replace("'", '"')
question_lower = question_lower.replace('which of these', 'what').replace('which', 'what')
"""quoted = re.findall('"([^"]*)"', question_lower) # Get all words in quotes
no_quote = question_lower
for quote in quoted:
no_quote = no_quote.replace(f"\"{quote}\"", "1placeholder1")
question_keywords = search.find_keywords(no_quote)
for quote in quoted:
question_keywords[question_keywords.index("1placeholder1")] = quote"""
#if 'which' in question_lower:
#question_lower += ' ({})'.format(' OR '.join(answers))
print('Search: "{}"'.format(question_lower))
smart_data, search_results = await search.search_google(question_lower, 5)
print('Smart Data: {}'.format(smart_data))
print('Search Results: {}'.format(search_results))
print('Searching smart Google data and website previews...')
search_text = [search.clean_html(x).lower().translate(punctuation_to_none) for x in smart_data]
counts = await self._search_method1(search_text, answers, reverse)
if counts:
self.googleResults.emit(counts, reverse)
else:
counts = await self._search_method2(search_text, answers, reverse)
print(counts)
if counts:
self.googleResults.emit(counts, reverse)
print('GOOGLE THINKS: {}'.format(min(counts, key=counts.get) if reverse else max(counts, key=counts.get)))
else:
self.googleResults.emit({k: 0 for k in answers}, False)
print('GOOGLE is not sure.')
print()
print('Searching full websites provided by Google...')
search_text = [x.translate(punctuation_to_none) for x in await search.get_clean_texts(search_results)]
counts = await self._search_method1(search_text, answers, reverse)
if counts:
self.pageResults.emit(counts, reverse)
else:
counts = await self._search_method2(search_text, answers, reverse)
if counts:
self.pageResults.emit(counts, reverse)
print('WEBSITES THINK: {}'.format(min(counts, key=counts.get) if reverse else max(counts, key=counts.get)))
else:
self.pageResults.emit({k: 0 for k in answers}, False)
print('WEBSITES are not sure.')
print(f"Search took {time.time() - start} seconds")
return ""
async def _search_method1(self, texts, answers, reverse):
"""
Returns the answer with the maximum/minimum number of exact occurrences in the texts.
:param texts: List of text to analyze
:param answers: List of answers
:param reverse: True if the best answer occurs the least, False otherwise
:return: Answer that occurs the most/least in the texts, empty string if there is a tie
"""
print("Running method 1")
counts = {answer.lower(): 0 for answer in answers}
for text in texts:
for answer in counts:
counts[answer] += len(re.findall(f"\\b{answer}\\b", text))
print(counts)
# If not all answers have count of 0 and the best value doesn't occur more than once, return the best answer
best_value = min(counts.values()) if reverse else max(counts.values())
if not all(c == 0 for c in counts.values()) and list(counts.values()).count(best_value) == 1:
return counts
return ""
async def _search_method2(self, texts, answers, reverse):
"""
Return the answer with the maximum/minimum number of keyword occurrences in the texts.
:param texts: List of text to analyze
:param answers: List of answers
:param reverse: True if the best answer occurs the least, False otherwise
:return: Answer whose keywords occur most/least in the texts
"""
print("Running method 2")
counts = {answer: {keyword: 0 for keyword in search.find_keywords(answer)} for answer in answers}
for text in texts:
for keyword_counts in counts.values():
for keyword in keyword_counts:
keyword_counts[keyword] += len(re.findall(f" {keyword} ", text))
print(counts)
counts_sum = {answer: sum(keyword_counts.values()) for answer, keyword_counts in counts.items()}
if not all(c == 0 for c in counts_sum.values()):
return counts_sum
return ""

8
requirements.txt Normal file
View File

@@ -0,0 +1,8 @@
aiohttp
bs4
nltk
unidecode
aiodns
cchardet
PyQt5
git+https://github.com/TheRandomDog/quamash.git

123
search.py Normal file
View File

@@ -0,0 +1,123 @@
import re
from html import unescape
from bs4 import BeautifulSoup
from nltk import word_tokenize
from nltk.corpus import stopwords
from nltk.tag.perceptron import PerceptronTagger
from nltk.tokenize import RegexpTokenizer
from unidecode import unidecode
import networking
STOP = set(stopwords.words("english")) - {"most", "least"}
tokenizer = RegexpTokenizer(r"\w+")
tagger = PerceptronTagger()
HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/59.0",
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate"}
GOOGLE_URL = "https://www.google.com/search?q={}&ie=utf-8&oe=utf-8&client=firefox-b-1-ab"
def find_keywords(words):
"""
Returns the list of words given without stopwords.
:param words: List of words
:return: Words without stopwords
"""
return [w for w in tokenizer.tokenize(words.lower()) if w not in STOP]
def find_nouns(text, num_words, reverse=False):
tokens = word_tokenize(text)
tags = tagger.tag(tokens)
print(tags)
tags = tags[:num_words] if not reverse else tags[-num_words:]
nouns = []
consecutive_nouns = []
for tag in tags:
tag_type = tag[1]
word = tag[0]
if "NN" not in tag_type and len(consecutive_nouns) > 0:
nouns.append(" ".join(consecutive_nouns))
consecutive_nouns = []
elif "NN" in tag_type:
consecutive_nouns.append(word)
if len(consecutive_nouns) > 0:
nouns.append(" ".join(consecutive_nouns))
return nouns
def get_google_links(page, num_results):
soup = BeautifulSoup(page, "html.parser")
results = soup.findAll("h3", {"class": "r"})
links = []
for r in results:
url = r.find("a")
if url is not None:
links.append(url["href"])
links = list(dict.fromkeys(links)) # Remove duplicates while preserving order
return links[:num_results]
def get_smart_data(page):
soup = BeautifulSoup(page, 'html.parser')
result = (
soup.find('div', {'class': 'Z0LcW'}) # Google Card
or soup.find('span', {'class': 'Y0NH2b'}) # Featured Snippet
)
if result:
return [result.text] + [r.text for r in soup.findAll('span', {'class': 'st'})]
else: # Descriptions generated by Google Search results are also pretty relevant.
return [r.text for r in soup.findAll('span', {'class': 'st'})]
async def search_google(question, num_results):
"""
Returns num_results urls from a google search of question.
:param question: Question to search
:param num_results: Number of results to return
:return: List of length num_results of urls retrieved from the search
"""
# Could use Google's Custom Search API here, limit of 100 queries per day
# result = service.cse().list(q=question, cx=CSE_ID, num=num_results).execute()
# return result["items"]
page = await networking.get_response(GOOGLE_URL.format(question), timeout=5, headers=HEADERS)
smart = get_smart_data(page)
return (smart, get_google_links(page, num_results))
async def multiple_search(questions, num_results):
queries = list(map(GOOGLE_URL.format, questions))
pages = await networking.get_responses(queries, timeout=5, headers=HEADERS)
link_list = [get_google_links(page, num_results) for page in pages]
return link_list
def clean_html(html):
# First we remove inline JavaScript/CSS:
cleaned = re.sub(r"(?is)<(script|style).*?>.*?(</\1>)", "", html.strip())
# Then we remove html comments. This has to be done before removing regular
# tags since comments can contain '>' characters.
cleaned = re.sub(r"(?s)<!--(.*?)-->[\n]?", "", cleaned)
# Next we can remove the remaining tags:
cleaned = re.sub(r"(?s)<.*?>", " ", cleaned)
# Finally, we deal with whitespace
cleaned = re.sub(r"&nbsp;", " ", cleaned)
cleaned = re.sub(r"\n", " ", cleaned)
cleaned = re.sub(r"\s\s+", " ", cleaned)
return unidecode(unescape(cleaned.strip()))
async def get_clean_texts(urls, timeout=2.5, headers=HEADERS):
responses = await networking.get_responses(urls, timeout, headers)
return [clean_html(r).lower() for r in responses]

66
stream.ini Normal file
View File

@@ -0,0 +1,66 @@
[DEFAULT]
screencap_origin: 0,0
screencap_res: 1280x720
screencap_fps: 30
audiofs: 44100
keyframe_sec: 2
audio_bps: 128k
preset: ultrafast
exe: ffmpeg
timelimit:
# indexed by sys.platform
[win32]
vcap: gdigrab
videochan: desktop
#vcap: dshow
#videochan: video="UScreenCapture"
#acap: dshow
#audiochan: audio="Internal Microphone"
hcam: dshow
# per-site config
[youtube]
server: rtmp://a.rtmp.youtube.com/live2/
key: mzv1-8h3w-mamv-eedw
[periscope]
video_kbps: 800
audio_bps: 64k
server: rtmp://va.pscp.tv:80/x/
key: periscope.key
[facebook]
server: rtmp://live-api.facebook.com:80/rtmp/
key:
[restream.io]
audio_bps: 64k
video_kbps: 800
server: rtmp://us-east.restream.io/live/
key: restreamio.key
[twitch]
audio_bps: 96k
server: rtmp://live-jfk.twitch.tv/app/
key:
[mixer]
keyframe_sec: 1
server: rtmp://ingest-wdc.mixer.com:1935/beam
key:
[ustream]
keyframe_sec: 1
audio_bps: 128k
server:
key:
[vimeo]
server: rtmp://rtmp.cloud.vimeo.com/
key:
[file]
video_kbps: 2000
screencap_res: 720x420
#audio_bps:

1440
trivia.csv Normal file

File diff suppressed because it is too large Load Diff

2
trivia2.csv Normal file
View File

@@ -0,0 +1,2 @@
"question","options","correct_answer","game"
" Which of these women married Ellen DeGeneres?","['k.d. Lang', 'Portia de ROSSI', 'Shannen Doherty']","1","KO Trivia"
1 question options correct_answer game
2 Which of these women married Ellen DeGeneres? ['k.d. Lang', 'Portia de ROSSI', 'Shannen Doherty'] 1 KO Trivia

7177
vlc.py Normal file

File diff suppressed because it is too large Load Diff