from datetime import datetime,timezone import webbrowser from urllib.parse import quote_plus from urllib.request import urlretrieve from threading import Thread from os import makedirs,path import http.server import json def download_files(): if not path.exists(script_path()+'swisseph/ephe/'): makedirs(script_path()+'swisseph/ephe/') #if not path.exists(script_path()+'swisseph/ephe/sefstars.txt'): # print('downloading sefstars.txt') # 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') zodiac_signs = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"] def get_planet_info(date): import swisseph as swe # make sure location is set in THIS thread swe.set_ephe_path(script_path()+'swisseph/ephe/') swe.set_sid_mode(swe.SIDM_LAHIRI) # Get current UTC time julian_day = swe.julday(date.year, date.month, date.day) # List of planets (Swiss Ephemeris IDs) planets = { "The Sun": swe.SUN, "The Moon": swe.MOON, "Mercury": swe.MERCURY, "Venus": swe.VENUS, "Mars": swe.MARS, "Jupiter": swe.JUPITER, "Saturn": swe.SATURN, "Uranus": swe.URANUS, "Neptune": swe.NEPTUNE, "Pluto": swe.PLUTO, "Chiron": swe.CHIRON, "North Node":swe.TRUE_NODE } retrograde_info = [] positions_info = {} for name, planet_id in planets.items(): # Get planet position (longitude) and speed pos, flags = swe.calc_ut(julian_day, planet_id, swe.FLG_SPEED) # Check if retrograde (speed is negative) is_retrograde = pos[3] < 0 if is_retrograde: if name=='North Node': pass else: retrograde_info.append(name) # Get zodiac sign (0-360° -> Aries to Pisces) sign_num = int(pos[0] // 30) # 30° per sign positions_info[name]=sign_num if name=='North Node': # rotate sign 180 dagrees south_pos=pos[0]+180 if(south_pos>360): south_pos-=360 # calculate South Node sign sign_num = int(south_pos // 30) positions_info['South Node']=sign_num return { 'positions': positions_info, 'retrograde': retrograde_info, } def get_planet_info_formatted(date=datetime.utcnow()): planets=get_planet_info(date) match len(planets['retrograde']): case 0: info='' case 1: info=planets['retrograde'][0]+' is in Retrograde' case 2: info=' and '.join(planets['retrograde'])+' are in Retrograde' case _: info=', '.join(planets['retrograde'][0:-1])+', and '+planets['retrograde'][-1]+' are in Retrograde' positions_info=[] for planet,position in planets['positions'].items(): positions_info.append(planet+' is in '+zodiac_signs[position]) info+='\n'+', '.join(positions_info[0:2]) info+='\n'+', '.join(positions_info[2:5]) info+='\n'+', '.join(positions_info[5:8]) info+='\n'+', '.join(positions_info[8:11]) info+='\n'+', '.join(positions_info[11:13]) return info def get_transits(): seconds_in_1_day=86400 now=datetime.utcnow() positions_today=get_planet_info(now) position_updates={} days=1 date_format='%b %-d' while len(position_updates)<12 and days<29: future_timestamp=now.timestamp()+(days*seconds_in_1_day) future_date=datetime.fromtimestamp(future_timestamp,timezone.utc) positions_future=get_planet_info(future_date) for planet in positions_future['retrograde']: if planet not in positions_today['retrograde'] and planet not in position_updates.keys(): position_updates[planet]='entering Retrograde on '+future_date.strftime(date_format) for planet in positions_today['retrograde']: if planet not in positions_future['retrograde'] and planet not in position_updates.keys(): position_updates[planet]='exiting Retrograde on '+future_date.strftime(date_format) for planet in positions_future['positions']: if positions_today['positions'][planet]!=positions_future['positions'][planet] and planet not in position_updates.keys(): position_updates[planet]='entering '+zodiac_signs[positions_future['positions'][planet]]+' on '+future_date.strftime(date_format) days+=1 return position_updates def get_transits_formatted(): transits=get_transits() info='' for planet,transit in transits.items(): info+='\n'+planet+' is '+transit return info async def load_tokens(): global obs_settings obs_tokens=obspython.obs_data_get_obj(obs_settings,'tokens') obs_tokens=obspython.obs_data_get_json(obs_tokens) 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): 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): global adapter, scopes webbrowser.open(adapter.get_authorization_url(scopes=scopes),2,True) # https://docs.obsproject.com/scripting#script-function-exports 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' def script_properties(): import obspython 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) return properties def script_load(settings): import sys global obs_settings global save if not obspython.obs_data_get_bool(settings,'pip_done'): import pip pip.main(['install','-qqq','pyswisseph','twitchio','twitchio[starlette]','--target',script_path()]) obspython.obs_data_set_bool(settings,'pip_done',True) sys.path.append(script_path()) download_files() obs_settings=settings def script_unload(): global client import asyncio asyncio.run(client.close()) def script_update(settings): 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__': def script_path(): return path.dirname(__file__)+'/' download_files() print(get_planet_info_formatted()) print(get_transits_formatted())