switch to twitch.py and other improvements

This commit is contained in:
2025-07-19 01:31:50 -07:00
parent 4102e8e672
commit 43e3f632cc
4 changed files with 139 additions and 129 deletions
+3 -1
View File
@@ -1,3 +1,5 @@
_* _*
.venv .venv
swisseph ephe
pip
tokens.json
Regular → Executable
+135 -126
View File
@@ -1,29 +1,26 @@
#!/usr/bin/env python3.11
from datetime import datetime,timezone from datetime import datetime,timezone
import webbrowser
from urllib.parse import quote_plus from urllib.parse import quote_plus
from urllib.request import urlretrieve from urllib.request import urlretrieve
from threading import Thread from threading import Thread
from os import makedirs,path from os import makedirs,path
import http.server import json,sys,webbrowser,importlib
import json
def download_files(): def dependency_setup():
if not path.exists(script_path()+'swisseph/ephe/'): print('checking dependencies')
makedirs(script_path()+'swisseph/ephe/') from pip._internal.cli.main import main as pip
#if not path.exists(script_path()+'swisseph/ephe/sefstars.txt'): pip(['install','-qq','pyswisseph','twitch.py','--target',script_path()+'pip'])
# print('downloading sefstars.txt') sys.path.append(script_path()+'pip')
# urlretrieve('https://github.com/aloistr/swisseph/raw/refs/heads/master/ephe/sefstars.txt',script_path()+'swisseph/ephe/sefstars.txt')
if not path.exists(script_path()+'swisseph/ephe/seas_18.se1'):
print('downloading seas_18.se1')
urlretrieve('https://github.com/aloistr/swisseph/raw/refs/heads/master/ephe/seas_18.se1',script_path()+'swisseph/ephe/seas_18.se1')
#if not path.exists(script_path()+'swisseph/ephe/semo_18.se1'):
# print('downloadingh semo_18.se1')
# urlretrieve('https://github.com/aloistr/swisseph/raw/refs/heads/master/ephe/semo_18.se1',script_path()+'swisseph/ephe/semo_18.se1')
#if not path.exists(script_path()+'swisseph/ephe/sepl_18.se1'):
# print('downloading sepl_18.se1')
# urlretrieve('https://github.com/aloistr/swisseph/raw/refs/heads/master/ephe/sepl_18.se1',script_path()+'swisseph/ephe/sepl_18.se1')
print('swiss ephemeris files ready')
if not path.exists(script_path()+'ephe/seas_18.se1'):
print('downloading database')
makedirs(script_path()+'ephe')
urlretrieve('https://github.com/aloistr/swisseph/raw/refs/heads/master/ephe/seas_18.se1',script_path()+'ephe/seas_18.se1')
print('dependencies ready')
# Astrology functions
zodiac_signs = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", zodiac_signs = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
"Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"] "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"]
@@ -31,7 +28,7 @@ zodiac_signs = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
def get_planet_info(date): def get_planet_info(date):
import swisseph as swe import swisseph as swe
# make sure location is set in THIS thread # make sure location is set in THIS thread
swe.set_ephe_path(script_path()+'swisseph/ephe/') swe.set_ephe_path(script_path()+'ephe/')
swe.set_sid_mode(swe.SIDM_LAHIRI) swe.set_sid_mode(swe.SIDM_LAHIRI)
# Get current UTC time # Get current UTC time
julian_day = swe.julday(date.year, date.month, date.day) julian_day = swe.julday(date.year, date.month, date.day)
@@ -92,21 +89,21 @@ def get_planet_info_formatted(date=datetime.utcnow()):
case 0: case 0:
info='' info=''
case 1: case 1:
info=planets['retrograde'][0]+' is in Retrograde' info=planets['retrograde'][0]+' is in Retrograde\n'
case 2: case 2:
info=' and '.join(planets['retrograde'])+' are in Retrograde' info=' and '.join(planets['retrograde'])+' are in Retrograde\n'
case _: case _:
info=', '.join(planets['retrograde'][0:-1])+', and '+planets['retrograde'][-1]+' are in Retrograde' info=', '.join(planets['retrograde'][0:-1])+', and '+planets['retrograde'][-1]+' are in Retrograde\n'
positions_info=[] positions_info=[]
for planet,position in planets['positions'].items(): for planet,position in planets['positions'].items():
positions_info.append(planet+' is in '+zodiac_signs[position]) positions_info.append(planet+' is in '+zodiac_signs[position])
info+='\n'+', '.join(positions_info[0:2]) info+=', '.join(positions_info[0:2])+'\n'
info+='\n'+', '.join(positions_info[2:5]) info+=', '.join(positions_info[2:5])+'\n'
info+='\n'+', '.join(positions_info[5:8]) info+=', '.join(positions_info[5:8])+'\n'
info+='\n'+', '.join(positions_info[8:11]) info+=', '.join(positions_info[8:11])+'\n'
info+='\n'+', '.join(positions_info[11:13]) info+=', '.join(positions_info[11:13])+'\n'
return info return info
def get_transits(): def get_transits():
@@ -136,126 +133,138 @@ def get_transits_formatted():
transits=get_transits() transits=get_transits()
info='' info=''
for planet,transit in transits.items(): for planet,transit in transits.items():
info+='\n'+planet+' is '+transit info+=planet+' is '+transit+'\n'
return info return info
async def load_tokens(): # Twitch functions
global obs_settings
obs_tokens=obspython.obs_data_get_obj(obs_settings,'tokens')
obs_tokens=obspython.obs_data_get_json(obs_tokens)
if not obs_tokens:
return
obs_tokens=json.loads(obs_tokens)
for token in obs_tokens.values():
await client.add_token(token['access'],token['refresh'])
async def save_tokens():
global obs_settings
tokens={}
for user_id in client.tokens:
token={
'access':client.tokens[user_id]['token'],
'refresh':client.tokens[user_id]['refresh'],
}
tokens[user_id]=token
obs_tokens=json.dumps(tokens)
obs_tokens=obspython.obs_data_create_from_json(obs_tokens)
obspython.obs_data_set_obj(obs_settings,'tokens',obs_tokens)
async def setup_hook():
import twitchio
for user_id in client.tokens:
await client.subscribe_websocket(twitchio.eventsub.ChatMessageSubscription(broadcaster_user_id=user_id,user_id=user_id),token_for=user_id)
print('connected to channel '+user_id)
print('astrolobot setup complete')
async def event_message(event):
if(event.text=='!astrology'):
info = get_planet_info_formatted()
for line in info.splitlines(False):
if line:
await event.broadcaster.send_message(line,event.broadcaster)
if(event.text=='!transits'):
info = get_transits_formatted()
for line in info.splitlines(False):
if line:
await event.broadcaster.send_message(line,event.broadcaster)
async def event_oauth_authorized(event):
global client
client.add_token(event['access_token'],event['refresh_token'])
await setup_hook()
def main():
import twitchio
global client, client_id, client_secret, adapter, scopes
#chdir(script_path())
scopes = twitchio.Scopes(['user:read:chat','user:write:chat','user:bot','channel:bot'])
adapter= twitchio.web.AiohttpAdapter(port=8523)
client = twitchio.Client(client_id=client_id,client_secret=client_secret,scopes=scopes,adapter=adapter)
client.event_message=event_message
client.event_oauth_authorized=event_oauth_authorized
client.setup_hook=setup_hook
client.save_tokens=save_tokens
client.load_tokens=load_tokens
client.run()
twitchio_thread=Thread(target=main,daemon=True)
def get_client_info(*args):
webbrowser.open('https://dev.twitch.tv/console/apps')
def login(*args): def login(*args):
global adapter, scopes global device_code
webbrowser.open(adapter.get_authorization_url(scopes=scopes),2,True) webbrowser.open('https://www.twitch.tv/activate?public=true&device-code='+device_code)
def save_tokens(access_token,refresh_token):
with open(script_path()+'tokens.json','w') as file:
json.dump(
{
'access_token':access_token,
'refresh_token':refresh_token,
},
file
)
def load_tokens():
try:
with open(script_path()+'tokens.json','r') as file:
return json.load(file)
except FileNotFoundError:
return {}
def main():
from twitch import Client
from twitch.types import eventsub
from twitch.ext.oauth import DeviceAuthFlow, Scopes
global client
client = Client(client_id='h08yimv2stqxci85tpfh5k7t16an2u')
tokens=load_tokens().values()
DeviceAuthFlow(
client=client,
scopes=[
Scopes.CHANNEL_BOT,
Scopes.USER_BOT,
Scopes.USER_READ_CHAT,
Scopes.USER_WRITE_CHAT,
],
wrap_run=len(tokens)==0
)
@client.event
async def on_code(code: str):
global device_code, obs_settings
device_code=code
if obs_settings == None:
login()
@client.event
async def on_auth(access_token: str, refresh_token: str):
save_tokens(access_token,refresh_token)
@client.event
async def on_ready() -> None:
print('astrolobot ready')
@client.event
async def on_chat_message(data: eventsub.chat.MessageEvent):
if data['message']['text']=='!astrology':
for line in get_planet_info_formatted().splitlines(False):
await client.channel.chat.send_message(line,data['message_id'])
return
if data['message']['text']=='!transits':
for line in get_transits_formatted().splitlines(False):
await client.channel.chat.send_message(line,data['message_id'])
client.run(*tokens)
# OBS functions
obs_settings=None
def script_main():
dependency_setup()
main()
def obs_load_tokens():
import obspython
access_token=obspython.obs_data_get_string(obs_settings,'access_token')
refresh_token=obspython.obs_data_get_string(obs_settings,'refresh_token')
if not (access_token or refresh_token):
print('astrolobot: login required',file=sys.stderr)
return {}
else:
return {
'access_token':access_token,
'refresh_token':refresh_token,
}
def obs_save_tokens(access_token,refresh_token):
import obspython
obspython.obs_data_set_string(obs_settings,'access_token',access_token)
obspython.obs_data_set_string(obs_settings,'refresh_token',refresh_token)
# https://docs.obsproject.com/scripting#script-function-exports # https://docs.obsproject.com/scripting#script-function-exports
def script_description(): def script_description():
return 'adds the !astrology command to your twitch channel, providing the current star signs of the planets and whether they are in retrograde' return 'adds the !astrology and !transits commands to your twitch channel, providing the current and upcoming star signs and retrograde status of the planets'
def script_properties(): def script_properties():
import obspython import obspython
properties=obspython.obs_properties_create() properties=obspython.obs_properties_create()
obspython.obs_properties_set_flags(properties,obspython.OBS_PROPERTIES_DEFER_UPDATE)
obspython.obs_properties_add_text(properties,'client_id','Client ID',obspython.OBS_TEXT_PASSWORD)
obspython.obs_properties_add_text(properties,'client_secret','Client Secret',obspython.OBS_TEXT_PASSWORD)
obspython.obs_properties_add_button(properties,'client_info','Find Client Info',get_client_info)
obspython.obs_properties_add_button(properties,'login','Login to Twitch',login) obspython.obs_properties_add_button(properties,'login','Login to Twitch',login)
return properties return properties
def script_load(settings): def script_load(settings):
import sys global obs_settings, save_tokens, load_tokens
global obs_settings
global save
sys.path.append(script_path())
try:
import twitchio, swisseph
except:
import pip
pip.main(['install','-qqq','pyswisseph','twitchio','twitchio[starlette]','--target',script_path()])
download_files()
obs_settings=settings obs_settings=settings
save_tokens=obs_save_tokens
load_tokens=obs_load_tokens
def isatty():
return False
sys.stdout.isatty=isatty
Thread(target=script_main,daemon=True).start()
def script_unload(): def script_unload():
global client try:
import asyncio import asyncio
asyncio.run(client.close()) global client
asyncio.run(client.close())
except NameError or ImportError:
pass
def script_update(settings): # test functions
global client_id,client_secret
client_id=obspython.obs_data_get_string(settings,'client_id')
client_secret=obspython.obs_data_get_string(settings,'client_secret')
if client_id and client_secret:
twitchio_thread.start()
if __name__ == '__main__': if __name__ == '__main__':
def script_path(): def script_path():
return path.dirname(__file__)+'/' return path.dirname(__file__)+'/'
download_files() dependency_setup()
print(get_planet_info_formatted()) print(get_planet_info_formatted())
print(get_transits_formatted()) print(get_transits_formatted())
main()
+1 -1
View File
@@ -1,2 +1,2 @@
pyswisseph pyswisseph
twitchio twitch.py
Submodule swisseph deleted from d8c10a91ff