Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework twitch auth to use only user #13

Merged
merged 3 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 33 additions & 24 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ name: Build

on:
workflow_dispatch:
pull_request:
branches:
- main
push:
tags:
- "v*.*.*"
Expand Down Expand Up @@ -45,10 +48,16 @@ jobs:
python -m pip install --upgrade pip pyinstaller
pip install -r requirements.txt

- name: Setup Env Vars
run: |
echo "REF_NAME=${GITHUB_REF_NAME//\//_}" >> $GITHUB_ENV
env:
GITHUB_REF_NAME: ${{ github.ref_name }}

- name: Release Versioning
if: startsWith(github.ref, 'refs/tags/')
run: |
echo "__version__='${{github.ref_name}}'" > src/_version.py
echo "__version__='${{env.REF_NAME}}'" > src/_version.py

- name: Nightly Versioning
if: true && !startsWith(github.ref, 'refs/tags/')
Expand All @@ -58,66 +67,66 @@ jobs:
- name: Build with pyinstaller
if: true && !startsWith(github.ref, 'refs/tags/')
# --noconsole # TODO: update code to log to file if frozen, impl rolling logs in local directory
run: pyinstaller --icon=images/logo.ico --onefile --collect-binaries python312.dll --hidden-import=aiosqlite --hidden-import=pyttsx4.drivers --hidden-import=pyttsx4.drivers.sapi5 --distpath dist/twitchchatdnd/${{ matrix.os }}-${{github.ref_name}}/ --name=twitchchatdnd-nightly src/main.py
run: pyinstaller --icon=images/logo.ico --onefile --collect-binaries python312.dll --hidden-import=aiosqlite --hidden-import=pyttsx4.drivers --hidden-import=pyttsx4.drivers.sapi5 --distpath dist/twitchchatdnd/${{ matrix.os }}-${{env.REF_NAME}}/ --name=twitchchatdnd-nightly src/main.py

- name: Release Build with pyinstaller
if: startsWith(github.ref, 'refs/tags/')
# --noconsole
run: pyinstaller --icon=images/logo.ico --onefile --collect-binaries python312.dll --hidden-import=aiosqlite --hidden-import=pyttsx4.drivers --hidden-import=pyttsx4.drivers.sapi5 --distpath dist/twitchchatdnd/${{ matrix.os }}-${{github.ref_name}}/ --name=twitchchatdnd src/main.py
run: pyinstaller --icon=images/logo.ico --onefile --collect-binaries python312.dll --hidden-import=aiosqlite --hidden-import=pyttsx4.drivers --hidden-import=pyttsx4.drivers.sapi5 --distpath dist/twitchchatdnd/${{ matrix.os }}-${{env.REF_NAME}}/ --name=twitchchatdnd src/main.py

- name: Copy Resources
run: |
mkdir dist/twitchchatdnd/${{ matrix.os }}-${{github.ref_name}}/resources
mkdir dist/twitchchatdnd/${{ matrix.os }}-${{github.ref_name}}/resources/images
mkdir dist/twitchchatdnd/${{ matrix.os }}-${{github.ref_name}}/resources/server
mkdir dist/twitchchatdnd/${{ matrix.os }}-${{github.ref_name}}/resources/server/static
mv images/* dist/twitchchatdnd/${{ matrix.os }}-${{github.ref_name}}/resources/images
mv src/server/static/* dist/twitchchatdnd/${{ matrix.os }}-${{github.ref_name}}/resources/server/static
mkdir dist/twitchchatdnd/${{ matrix.os }}-${{env.REF_NAME}}/resources
mkdir dist/twitchchatdnd/${{ matrix.os }}-${{env.REF_NAME}}/resources/images
mkdir dist/twitchchatdnd/${{ matrix.os }}-${{env.REF_NAME}}/resources/server
mkdir dist/twitchchatdnd/${{ matrix.os }}-${{env.REF_NAME}}/resources/server/static
mv images/* dist/twitchchatdnd/${{ matrix.os }}-${{env.REF_NAME}}/resources/images
mv src/server/static/* dist/twitchchatdnd/${{ matrix.os }}-${{env.REF_NAME}}/resources/server/static


- name: Deploy Artifacts
uses: actions/upload-artifact@v4
if: true && !startsWith(github.ref, 'refs/tags/')
with:
name: twitchchatdnd-${{matrix.os}}-latest
path: dist/twitchchatdnd/${{matrix.os}}-${{github.ref_name}}/
path: dist/twitchchatdnd/${{matrix.os}}-${{env.REF_NAME}}/
if-no-files-found: error
retention-days: 20

- name: Release Rename
if: startsWith(github.ref, 'refs/tags/') && !startsWith(matrix.os, 'windows')
run: |
mkdir twitchchatdnd-${{github.ref_name}}
mv dist/twitchchatdnd/${{matrix.os}}-${{github.ref_name}}/* twitchchatdnd-${{github.ref_name}}/
zip -r twitchchatdnd-${{matrix.os}}-${{github.ref_name}}.zip twitchchatdnd-${{github.ref_name}}
mkdir twitchchatdnd-${{env.REF_NAME}}
mv dist/twitchchatdnd/${{matrix.os}}-${{env.REF_NAME}}/* twitchchatdnd-${{env.REF_NAME}}/
zip -r twitchchatdnd-${{matrix.os}}-${{env.REF_NAME}}.zip twitchchatdnd-${{env.REF_NAME}}

- name: Release Rename - Windows
if: startsWith(github.ref, 'refs/tags/') && startsWith(matrix.os, 'windows')
run: |
mkdir twitchchatdnd-${{github.ref_name}}
mv dist/twitchchatdnd/${{matrix.os}}-${{github.ref_name}}/* twitchchatdnd-${{github.ref_name}}/
7z a twitchchatdnd-${{matrix.os}}-${{github.ref_name}}.zip twitchchatdnd-${{github.ref_name}}
mkdir twitchchatdnd-${{env.REF_NAME}}
mv dist/twitchchatdnd/${{matrix.os}}-${{env.REF_NAME}}/* twitchchatdnd-${{env.REF_NAME}}/
7z a twitchchatdnd-${{matrix.os}}-${{env.REF_NAME}}.zip twitchchatdnd-${{env.REF_NAME}}

- name: Release Artifacts
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
token: ${{ secrets.TCDND_GITHUB_TOKEN }}
files: twitchchatdnd-${{matrix.os}}-${{github.ref_name}}.zip
files: twitchchatdnd-${{matrix.os}}-${{env.REF_NAME}}.zip

- name: Nightly Release Rename
if: true && !startsWith(github.ref, 'refs/tags/') && !startsWith(matrix.os, 'windows')
run: |
mkdir twitchchatdnd-${{github.ref_name}}
mv dist/twitchchatdnd/${{matrix.os}}-${{github.ref_name}}/* twitchchatdnd-${{github.ref_name}}/
zip -r twitchchatdnd-${{matrix.os}}-${{github.ref_name}}.zip twitchchatdnd-${{github.ref_name}}
mkdir twitchchatdnd-${{env.REF_NAME}}
mv dist/twitchchatdnd/${{matrix.os}}-${{env.REF_NAME}}/* twitchchatdnd-${{env.REF_NAME}}/
zip -r twitchchatdnd-${{matrix.os}}-${{env.REF_NAME}}.zip twitchchatdnd-${{env.REF_NAME}}

- name: Nightly Rename - Windows
if: true && !startsWith(github.ref, 'refs/tags/') && startsWith(matrix.os, 'windows')
run: |
mkdir twitchchatdnd-${{github.ref_name}}
mv dist/twitchchatdnd/${{matrix.os}}-${{github.ref_name}}/* twitchchatdnd-${{github.ref_name}}/
7z a twitchchatdnd-${{matrix.os}}-${{github.ref_name}}.zip twitchchatdnd-${{github.ref_name}}
mkdir twitchchatdnd-${{env.REF_NAME}}
mv dist/twitchchatdnd/${{matrix.os}}-${{env.REF_NAME}}/* twitchchatdnd-${{env.REF_NAME}}/
7z a twitchchatdnd-${{matrix.os}}-${{env.REF_NAME}}.zip twitchchatdnd-${{env.REF_NAME}}

- name: Release nightly
uses: softprops/action-gh-release@v2
Expand All @@ -127,5 +136,5 @@ jobs:
prerelease: true
name: nightly
tag_name: nightly
files: twitchchatdnd-${{matrix.os}}-${{github.ref_name}}.zip
files: twitchchatdnd-${{matrix.os}}-${{env.REF_NAME}}.zip
fail_on_unmatched_files: true
15 changes: 4 additions & 11 deletions src/helpers/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import configparser
import base64

# ID is fine to ship
_client_id = base64.b64decode("dXp4NXFiOXNzZXl0dGtvZXF5cXdydmthOGdic3Br").decode('utf-8')

class TCDNDConfig(configparser.ConfigParser):

Expand Down Expand Up @@ -49,12 +52,6 @@ def setup(self, path: str):
if not self.has_option(section="TWITCH", option="channel"):
needs_init = True
self.set(section="TWITCH", option="channel", value="")
if not self.has_option(section="TWITCH", option="client_id"):
needs_init = True
self.set(section="TWITCH", option="client_id", value="")
if not self.has_option(section="TWITCH", option="client_secret"):
needs_init = True
self.set(section="TWITCH", option="client_secret", value="")

if not self.has_section("DND"):
needs_init = True
Expand All @@ -68,11 +65,7 @@ def setup(self, path: str):

@property
def twitch_auth(self) -> tuple[str, str]:
client_id = self.get(section='TWITCH', option='client_id', fallback='')
client_secret = self.get(section='TWITCH', option='client_secret', fallback='')
if client_id == '' or client_secret == '':
return None, None
return client_id.strip(), client_secret.strip()
return _client_id.strip()

@property
def cache_enabled(self) -> bool:
Expand Down
9 changes: 2 additions & 7 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
async def run_twitch():

async def try_setup():
while not all(config.twitch_auth):
while not config.twitch_auth:
await asyncio.sleep(5)
logger.info("Starting Twitch Client...")

Expand All @@ -51,13 +51,8 @@ async def try_setup():
if twitch_utils.twitch:
return True
except Exception as e:
logger.error(f"Invalid Twitch Client Id or Client Secret or other Twitch connection issue")
logger.error(f"Invalid Twitch Connection")
logger.error(e)
# only clear on twitch errors
if type(e) == TwitchAuthorizationException:
config.set(section="TWITCH", option="client_id", value='')
config.set(section="TWITCH", option="client_secret", value='')
config.write_updates()
return False

success = False
Expand Down
5 changes: 3 additions & 2 deletions src/twitch/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,11 @@ async def start(self):
self.twitch = None
SCOPES = [AuthScope.CHAT_READ, AuthScope.CHAT_EDIT] # TODO may need more as time goes on
try:
self.twitch = await Twitch(*self.config.twitch_auth)
self.twitch = await Twitch(app_id=self.config.twitch_auth, authenticate_app=False)
helper = UserAuthenticationStorageHelper(self.twitch, SCOPES, auth_generator_func=self._token_gen)
await helper.bind()
except:
except Exception as e:
logger.error(e)
twitchutils_twitch_on_connect_event.trigger([False, None])
raise
logger.info("Twitch connected")
Expand Down
109 changes: 25 additions & 84 deletions src/ui/tabs/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,64 +22,23 @@ def __init__(self, parent, config: Config, twitch_utils: TwitchUtils):
row=0
######### Twitch ##########
column=0
label = ctk.CTkLabel(self.parent, text="Twitch", font=header_font)
label = ctk.CTkLabel(self.parent, text="Twitch Bot", font=header_font)
label.grid(row=row, column=column, padx=10, pady=(30,2), sticky="ew")
column+=1
self.t_con_label = ctk.CTkLabel(self.parent, text="Disconnected", text_color="red")
self.t_con_label = ctk.CTkLabel(self.parent, text="Twitch Disconnected", text_color="red")
self.t_con_label.grid(row=row, column=column, padx=10, pady=(30,2))
column+=1
self.chat_con_label = ctk.CTkLabel(self.parent, text="Chat Disconnected", text_color="red")
self.chat_con_label.grid(row=row, column=column, padx=10, pady=(30,2))
chat_bot_on_connect.addListener(self._update_bot_connection)
column=0
row+=1

button = ctk.CTkButton(self.parent,height=30, text="Save", command=self._update_twitch_auth)
button = ctk.CTkButton(self.parent,height=30, text="Save", command=self._update_bot_settings)
button.grid(row=row, column=column, padx=10, pady=(20,10))
column+=1
chan_label = ctk.CTkLabel(self.parent, text="Channel")
id_label = ctk.CTkLabel(self.parent, text="Client Id")
secret_label = ctk.CTkLabel(self.parent, text="Client Secret")
chan_label.grid(row=row, column=column, padx=(10,10), pady=(10,2))
column+=1
id_label.grid(row=row, column=column, padx=(10, 10), pady=(10,2))
column+=1
secret_label.grid(row=row, column=column, padx=(10, 10), pady=(10,2))
column+=1

row+=1
column=1
self.channel_var = ctk.StringVar(value=self.config.get(section="TWITCH", option="channel", fallback=""))
twitch_channel = ctk.CTkEntry(self.parent, width=180, height=30, border_width=1, fg_color="white", placeholder_text="Channel", text_color="black", textvariable=self.channel_var)
twitch_channel.configure(justify="center")
self.clientid_var = ctk.StringVar(value=self.config.get(section="TWITCH", option="client_id", fallback=""))
twitch_client_id = ctk.CTkEntry(self.parent, width=160, height=30, border_width=1, fg_color="white", placeholder_text="Client Id", text_color="black", textvariable=self.clientid_var)
self.clientsecret_var = ctk.StringVar(value=self.config.get(section="TWITCH", option="client_secret", fallback=""))
twitch_client_secret = ctk.CTkEntry(self.parent, width=160, height=30, border_width=1, fg_color="white", placeholder_text="Client Secret", text_color="black", show="*", textvariable=self.clientsecret_var)

twitch_channel.grid(row=row, column=column, padx=(20,20), pady=(2, 20))
column+=1
twitch_client_id.grid(row=row, column=column, padx=(20,20), pady=(2, 20))
column+=1
twitch_client_secret.grid(row=row, column=column, padx=(20,20), pady=(2, 20))


twitchutils_twitch_on_connect_event.addListener(self._update_twitch_connect)
chat_on_channel_fetch.addListener(self._update_twitch_channel)

########################


######### BOT ##########
row += 1
column = 0
label_bot = ctk.CTkLabel(self.parent, text="Bot", font=header_font)
label_bot.grid(row=row, column=column, padx=10, pady=(40,10), sticky="ew")
column+=1
self.chat_con_label = ctk.CTkLabel(self.parent, text="Disconnected", text_color="red")
self.chat_con_label.grid(row=row, column=column, padx=10, pady=(40,2))
chat_bot_on_connect.addListener(self._update_bot_connection)
column=0
row+=1
bot_button = ctk.CTkButton(self.parent,height=30, text="Save", command=self._update_bot_settings)
bot_button.grid(row=row, column=column, padx=10, pady=(20,10))

column+=1
prefix_label = ctk.CTkLabel(self.parent, text="Command Prefix")
prefix_label.grid(row=row, column=column, padx=(10,10), pady=(10,2))
Expand All @@ -92,8 +51,14 @@ def __init__(self, parent, config: Config, twitch_utils: TwitchUtils):

row+=1
column=1
self.channel_var = ctk.StringVar(value=self.config.get(section="TWITCH", option="channel", fallback=""))
twitch_channel = ctk.CTkEntry(self.parent, width=180, height=30, border_width=1, fg_color="white", placeholder_text="Channel", text_color="black", textvariable=self.channel_var)
twitch_channel.configure(justify="center")

twitch_channel.grid(row=row, column=column, padx=(20,20), pady=(2, 20))
column+=1
self.prefix_var = ctk.StringVar(value=self.config.get(section="BOT", option="prefix", fallback='!').strip()[0])
prefix_options = ctk.CTkSegmentedButton(self.parent, values=['!', '~', '+', '&'], variable=self.prefix_var)#command=self._update_prefix)
prefix_options = ctk.CTkSegmentedButton(self.parent, values=['!', '~', '+', '&'], variable=self.prefix_var)
prefix_options.grid(row=row, column=column, padx=(20,20), pady=(2, 20))
column += 1
self.join_cmd_var = ctk.StringVar(value=self.config.get(section="BOT", option="join_command", fallback=''))
Expand All @@ -106,6 +71,8 @@ def __init__(self, parent, config: Config, twitch_utils: TwitchUtils):
speak_cmd_entry.grid(row=row, column=column, padx=(20,20), pady=(2, 20))
speak_cmd_entry.configure(justify="center")

twitchutils_twitch_on_connect_event.addListener(self._update_twitch_connect)

########################

####### Web Srv ########
Expand All @@ -127,51 +94,25 @@ def __init__(self, parent, config: Config, twitch_utils: TwitchUtils):


def _update_bot_settings(self):
self.chat_con_label.configure(text="Chat Connecting...", text_color="yellow")
self.config.set(section="BOT", option="prefix", value=str(self.prefix_var.get()))
self.config.set(section="BOT", option="speak_command", value=self.speak_cmd_var.get().strip())
self.config.set(section="BOT", option="join_command", value=self.join_cmd_var.get().strip())
self.config.set(section="TWITCH", option="channel", value=self.channel_var.get())
self.config.write_updates()
ui_settings_bot_settings_update_event.trigger()


def _update_twitch_connect(self, status: bool, twitchutils = None):
if status:
self.t_con_label.configure(text="Connected", text_color="green")
else:
if self.clientid_var.get():
self.clientid_var.set("")
if self.clientsecret_var.get():
self.clientsecret_var.set("")
self.t_con_label.configure(text="Disconnected", text_color="red")
self.parent.focus()
ui_settings_twitch_channel_update_event.trigger([True, self.twitch_utils, 5])


def _update_twitch_channel(self, status: bool):
def _update_bot_connection(self, status: bool):
if status:
self.t_con_label.configure(text="Connected", text_color="green")
self.chat_con_label.configure(text="Chat Connected", text_color="green")
else:
if self.channel_var.get():
self.channel_var.set("")
self.t_con_label.configure(text="Disconnected", text_color="red")
self.chat_con_label.configure(text="Chat Disconnected", text_color="red")
self.parent.focus()


def _update_bot_connection(self, status: bool):
def _update_twitch_connect(self, status: bool, twitchutils = None):
if status:
self.chat_con_label.configure(text="Connected", text_color="green")
self.t_con_label.configure(text="Twitch Connected", text_color="green")
else:
self.chat_con_label.configure(text="Disconnected", text_color="red")
self.t_con_label.configure(text="Twitch Disconnected", text_color="red")
self.parent.focus()


def _update_twitch_auth(self):
if not self.config.has_section("TWITCH"):
self.config.add_section("TWITCH")
self.config.set(section="TWITCH", option="client_id", value=self.clientid_var.get())
self.config.set(section="TWITCH", option="client_secret", value=self.clientsecret_var.get())
self.config.set(section="TWITCH", option="channel", value=self.channel_var.get())
self.t_con_label.configure(text="Connecting...", text_color="yellow")
self.chat_con_label.configure(text="Connecting...", text_color="yellow")
self.config.write_updates()
ui_settings_twitch_auth_update_event.trigger()
ui_settings_twitch_channel_update_event.trigger([True, self.twitch_utils, 5])