21 Commits
Author SHA1 Message Date
sugoidogo d1892a241f fix typo 2025-07-20 23:15:26 -07:00
sugoidogo eb045fa94a fix update function args 2025-07-20 23:14:19 -07:00
sugoidogo f586906a08 add update button to obs 2025-07-20 23:12:04 -07:00
sugoidogo 9278d32549 add command name to settings 2025-07-20 22:51:06 -07:00
josephsmendoza 2784872d12 fix early login request 2025-07-20 15:57:49 -07:00
josephsmendoza 9bfcc1a8a3 gracefully fail refreshing tokens 2025-07-20 15:55:39 -07:00
sugoidogo b730599d7e refresh tokens and log token activity 2025-07-20 02:59:32 -07:00
sugoidogo 32115c5a02 use universal strftime format 2025-07-19 01:57:37 -07:00
sugoidogo 43e3f632cc switch to twitch.py and other improvements 2025-07-19 01:31:50 -07:00
josephsmendoza 4102e8e672 fix overridden event 2025-07-18 17:10:36 -07:00
sugoidogo f0ca3fd16c fix dependency check 2025-07-18 01:39:05 -07:00
sugoidogo df27549942 better dependency check 2025-07-18 01:35:27 -07:00
sugoidogo cdb521b8ad fix loading with no tokens 2025-07-18 01:19:02 -07:00
sugoidogo 6907dacf89 fix library usage for OBS cold start 2025-07-18 01:03:56 -07:00
sugoidogo 06cc06476a add !transits command 2025-07-18 00:25:48 -07:00
sugoidogo 05dfb7215b updated output format 2025-07-16 21:18:54 -07:00
sugoidogo 11a713edbb install dependencies to astrolobot folder 2025-07-16 20:21:55 -07:00
josephsmendoza eba9505d1a more readable output format 2025-07-16 11:36:28 -07:00
josephsmendoza 241394fe2d add simple test without OBS 2025-07-16 11:36:09 -07:00
josephsmendoza f8347eeed8 download swiss ephemeris files as needed 2025-07-16 11:35:34 -07:00
josephsmendoza ee9fb1a969 initial commit 2025-07-16 03:49:20 -07:00
3 changed files with 104 additions and 391 deletions
-23
View File
@@ -1,23 +0,0 @@
name: Release
on:
push:
tags:
- "*"
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- run: git log $(git describe --tags --abbrev=0 HEAD~1)..HEAD --oneline > changelog.txt
- uses: softprops/action-gh-release@v2
with:
make_latest: true
body_path: changelog.txt
files: astrolobot.py
+103 -367
View File
@@ -5,50 +5,36 @@ from urllib.parse import quote_plus
from urllib.request import urlretrieve, urlopen, Request, HTTPError from urllib.request import urlretrieve, urlopen, Request, HTTPError
from threading import Thread from threading import Thread
from os import makedirs,path from os import makedirs,path
from functools import cache
import json,sys,webbrowser,importlib,tomllib import json,sys,webbrowser,importlib,tomllib
def update(): def update(*args):
print('checking for updates...') print('checking for updates...')
try: with open(script_path()+'astrolobot.py','r') as file:
with open(script_path()+'astrolobot.py','r') as file: current_script=file.read()
current_script=file.read() urlretrieve(
urlretrieve( 'https://github.com/sugoidogo/astrolobot/releases/latest/download/astrolobot.py',
'https://gitea.sugoidogo.com/SugoiDogo/astrolobot/releases/download/latest/astrolobot.py', script_path()+'astrolobot.py'
script_path()+'astrolobot.py' )
) with open(script_path()+'astrolobot.py','r') as file:
with open(script_path()+'astrolobot.py','r') as file: updated_script=file.read()
updated_script=file.read() if current_script==updated_script:
if current_script==updated_script: print('up to date')
print('up to date') else:
else: print('update downloaded, restart script to apply')
print('update downloaded, restart script to apply')
except:
print('update check failed, ignoring')
def dependency_setup(): def dependency_setup():
if not path.exists(script_path()+'pip'): print('checking dependencies')
print('installing dependencies') from pip._internal.cli.main import main as pip
from pip._internal.cli.main import main as pip pip(['install','-qq','pyswisseph','twitch.py','--target',script_path()+'pip'])
pip(['install','-qq','-r',script_path()+'/requirements.txt','--target',script_path()+'pip'])
sys.path.append(script_path()+'pip') sys.path.append(script_path()+'pip')
if not path.exists(script_path()+'ephe/seas_18.se1'): if not path.exists(script_path()+'ephe/seas_18.se1'):
print('downloading database') print('downloading database')
makedirs(script_path()+'ephe') makedirs(script_path()+'ephe')
urlretrieve('https://github.com/aloistr/swisseph/raw/refs/heads/master/ephe/seas_18.se1',script_path()+'ephe/seas_18.se1') urlretrieve('https://github.com/aloistr/swisseph/raw/refs/heads/master/ephe/seas_18.se1',script_path()+'ephe/seas_18.se1')
import swisseph as swe
swe.set_ephe_path(script_path()+'ephe/')
swe.set_sid_mode(swe.SIDM_LAHIRI)
print('dependencies ready') print('dependencies ready')
def today():
now=datetime.utcnow()
return datetime(now.year,now.month,now.day)
# Astrology functions # Astrology functions
def load_settings(): def load_settings():
@@ -59,24 +45,20 @@ def load_settings():
return { return {
'positions':'!positions', 'positions':'!positions',
'transits':'!transits', 'transits':'!transits',
'major_aspects':'!aspects major',
'minor_aspects':'!aspects minor',
'major_aspect_transits':'!aspects major transits',
'minor_aspect_transits':'!aspects minor transits',
} }
def get_zodiac(angle): zodiac_signs = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
zodiac = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"]
"Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"]
return zodiac[int(angle//30)]
def is_retrograde(speed): def get_planet_info(date):
return speed<0
@cache
def get_positions_raw(date=today()):
import swisseph as swe import swisseph as swe
# make sure location is set in THIS thread
swe.set_ephe_path(script_path()+'ephe/')
swe.set_sid_mode(swe.SIDM_LAHIRI)
# Get current UTC time
julian_day = swe.julday(date.year, date.month, date.day) julian_day = swe.julday(date.year, date.month, date.day)
# List of planets (Swiss Ephemeris IDs)
planets = { planets = {
"The Sun": swe.SUN, "The Sun": swe.SUN,
"The Moon": swe.MOON, "The Moon": swe.MOON,
@@ -91,303 +73,93 @@ def get_positions_raw(date=today()):
"Chiron": swe.CHIRON, "Chiron": swe.CHIRON,
"North Node":swe.TRUE_NODE "North Node":swe.TRUE_NODE
} }
positions={}
for planet_name, planet_id in planets.items(): retrograde_info = []
position, _ = swe.calc_ut(julian_day, planet_id, swe.FLG_SPEED) positions_info = {}
positions[planet_name]={
'angle':position[0],
'speed':position[3],
}
if planet_name=='North Node':
positions['South Node']={
'angle':(position[0]+180)%360,
'speed':position[3],
}
return positions 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)
@cache # Check if retrograde (speed is negative)
def get_positions(date=today()): is_retrograde = pos[3] < 0
positions={} if is_retrograde:
if name=='North Node':
pass
else:
retrograde_info.append(name)
for name, position_raw in get_positions_raw(date).items(): # Get zodiac sign (0-360° -> Aries to Pisces)
positions[name]={ sign_num = int(pos[0] // 30) # 30° per sign
'zodiac':get_zodiac(position_raw['angle']), positions_info[name]=sign_num
'retrograde':is_retrograde(position_raw['speed']),
}
return positions 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
def get_list_formatted(in_list,isare=False): return {
match len(in_list): 'positions': positions_info,
case 0: 'retrograde': retrograde_info,
output='Nothing' }
if isare:
output+=' is '
case 1:
output=in_list[0]
if isare:
output+=' is '
case 2:
output=' and '.join(in_list)
if isare:
output+=' are '
case _:
output=', '.join(in_list[0:-1])+', and '+in_list[-1]
if isare:
output+=' are '
return output
@cache def get_planet_info_formatted(date=datetime.utcnow()):
def get_positions_formatted(date=today()): planets=get_planet_info(date)
retrograde_planets=[]
position_strings=[] match len(planets['retrograde']):
case 0:
info=''
case 1:
info=planets['retrograde'][0]+' is in Retrograde\n'
case 2:
info=' and '.join(planets['retrograde'])+' are in Retrograde\n'
case _:
info=', '.join(planets['retrograde'][0:-1])+', and '+planets['retrograde'][-1]+' are in Retrograde\n'
for name, position in get_positions(date).items(): positions_info=[]
if(position['retrograde']): for planet,position in planets['positions'].items():
retrograde_planets.append(name) positions_info.append(planet+' is in '+zodiac_signs[position])
position_strings.append(name+' is in '+position['zodiac'])
positions_formatted=get_list_formatted(retrograde_planets,True)+'in Retrograde\n'
positions_formatted+=', '.join(position_strings[0:2])+'\n' info+=', '.join(positions_info[0:2])+'\n'
positions_formatted+=', '.join(position_strings[2:5])+'\n' info+=', '.join(positions_info[2:5])+'\n'
positions_formatted+=', '.join(position_strings[5:8])+'\n' info+=', '.join(positions_info[5:8])+'\n'
positions_formatted+=', '.join(position_strings[8:11])+'\n' info+=', '.join(positions_info[8:11])+'\n'
positions_formatted+=', '.join(position_strings[11:13])+'\n' info+=', '.join(positions_info[11:13])+'\n'
return info
return positions_formatted
seconds_in_1_day=86400 def get_transits():
seconds_in_1_day=86400
@cache now=datetime.utcnow()
def get_transits(date=today(), maxdays=28): positions_today=get_planet_info(now)
positions_now=get_positions(date) position_updates={}
transits={}
days=1 days=1
date_format='%b %d'
while len(transits)<12 and days<=maxdays: while len(position_updates)<12 and days<29:
future_timestamp=date.timestamp()+(days*seconds_in_1_day) future_timestamp=now.timestamp()+(days*seconds_in_1_day)
future_date=datetime.fromtimestamp(future_timestamp,timezone.utc) future_date=datetime.fromtimestamp(future_timestamp,timezone.utc)
positions_future=get_positions(future_date) positions_future=get_planet_info(future_date)
for name, position_future in positions_future.items(): for planet in positions_future['retrograde']:
position_now=positions_now[name] if planet not in positions_today['retrograde'] and planet not in position_updates.keys():
if name not in transits and position_now!=position_future: position_updates[planet]='entering Retrograde on '+future_date.strftime(date_format)
position_future['date']=future_date for planet in positions_today['retrograde']:
if position_now['zodiac']==position_future['zodiac']: if planet not in positions_future['retrograde'] and planet not in position_updates.keys():
position_future['zodiac']=None position_updates[planet]='exiting Retrograde on '+future_date.strftime(date_format)
if position_now['retrograde']==position_future['retrograde']: for planet in positions_future['positions']:
position_future['retrograde']=None if positions_today['positions'][planet]!=positions_future['positions'][planet] and planet not in position_updates.keys():
transits[name]=position_future position_updates[planet]='entering '+zodiac_signs[positions_future['positions'][planet]]+' on '+future_date.strftime(date_format)
days+=1 days+=1
return position_updates
return transits
date_format='%b %d' def get_transits_formatted():
transits=get_transits()
@cache info=''
def get_transits_formatted(date=today(), maxdays=28): for planet,transit in transits.items():
transits_formatted='' info+=planet+' is '+transit+'\n'
return info
for name,transit in get_transits(date, maxdays).items():
if transit['zodiac']!=None:
transits_formatted+=name+' is entering '+transit['zodiac']+' on '+transit['date'].strftime(date_format)+'\n'
if transit['retrograde']==True:
transits_formatted+=name+' is entering Retrograde on '+transit['date'].strftime(date_format)+'\n'
if transit['retrograde']==False:
transits_formatted+=name+' is exiting Retrograde on '+transit['date'].strftime(date_format)+'\n'
return transits_formatted
@cache
def get_aspects(date=today(), minor=False):
positions=get_positions_raw(date).copy()
major_aspects={
'conjuction':{'angle':0,'orb':10.0},
'oposition':{'angle':180,'orb':10.0},
'trine':{'angle':120,'orb':10.0},
'square':{'angle':90,'orb':10.0},
'sextile':{'angle':60,'orb':5.0},
}
minor_aspects={
'semi-sextile':{'angle':30,'orb':1.5},
'inconjunct':{'angle':150,'orb':3},
'semi-square':{'angle':45,'orb':3},
'trioctile':{'angle':135,'orb':3},
'quintile':{'angle':72,'orb':1},
'biquintile':{'angle':144,'orb':1},
}
aspects={}
if minor:
selected_aspects=minor_aspects
else:
selected_aspects=major_aspects
for aname, aposition in positions.copy().items():
del positions[aname]
if aname.endswith('Node'):
continue
planet_aspects={}
for bname, bposition in positions.items():
angle_diff=abs(aposition['angle']-bposition['angle'])
for aspect_name,aspect in selected_aspects.items():
aspect_max=(aspect['angle']+aspect['orb'])%360
aspect_min=(aspect['angle']-aspect['orb'])%360
if aspect_min<angle_diff<aspect_max:
try:
planet_aspects[aspect_name].append(bname)
except KeyError:
planet_aspects[aspect_name]=[bname]
if len(planet_aspects)!=0:
aspects[aname]=planet_aspects
return aspects
@cache
def get_aspects_formatted(date=today(),minor=False):
aspects_formatted=''
for planet,aspects in get_aspects(date,minor).items():
aspects_formatted+=planet+' is in '
aspects_list=[]
for aspect_name,aspect_planets in aspects.items():
aspects_list.append(aspect_name+' with '+get_list_formatted(aspect_planets))
aspects_formatted+=get_list_formatted(aspects_list)+'\n'
return aspects_formatted
@cache
def get_aspect_transits(date=today(),minor=False,maxdays=28):
date_now=today()
timestamp_now=date_now.timestamp()
aspects_now=get_aspects(date_now,minor)
pre_transits={}
days=0
def aspects_contains(aspects,planet_a,aspect=None,planet_b=None):
if planet_a not in aspects:
return False
elif aspect and aspect not in aspects[planet_a]:
return False
elif aspect and planet_b and planet_b not in aspects[planet_a][aspect]:
return False
return True
def ensure_pre_path(planet_a,aspect=None):
if planet_a not in pre_transits:
pre_transits[planet_a]={}
if aspect and aspect not in pre_transits[planet_a]:
pre_transits[planet_a][aspect]={}
def pre_planet_b(planet_a,aspect,planet_b,date_future,direction):
ensure_pre_path(planet_a,aspect)
pre_transits[planet_a][aspect][planet_b]={
'date':date_future,
'direction':direction
}
def pre_aspect(planet_a,aspect,planets,date_future,direction):
ensure_pre_path(planet_a)
pre_transits[planet_a][aspect]={}
for planet_b in planets:
pre_planet_b(planet_a,aspect,planet_b,date_future,direction)
def pre_planet_a(planet_a,aspects,date_future,direction):
pre_transits[planet_a]={}
for aspect,planets in aspects.items():
pre_aspect(planet_a,aspect,planets,date_future,direction)
while days<maxdays:
days+=1
timestamp_future=timestamp_now+(seconds_in_1_day*days)
date_future=datetime.fromtimestamp(timestamp_future,timezone.utc)
aspects_future=get_aspects(date_future)
for planet_a,aspects in aspects_now.items():
try:
pre_transits[planet_a]
except KeyError:
if not aspects_contains(aspects_future,planet_a):
pre_planet_a(planet_a,aspects,date_future,'exiting')
continue
for aspect, planets in aspects.items():
try:
pre_transits[planet_a][aspect]
except KeyError:
if not aspects_contains(aspects_future,planet_a,aspect):
pre_aspect(planet_a,aspect,planets,date_future,'exiting')
continue
for planet_b in planets:
try:
pre_transits[planet_a][aspect][planet_b]
except KeyError:
if not aspects_contains(aspects_future,planet_a,aspect,planet_b):
pre_planet_b(planet_a,aspect,planet_b,date_future,'exiting')
days=0
while days<maxdays:
days+=1
timestamp_future=timestamp_now+(seconds_in_1_day*days)
date_future=datetime.fromtimestamp(timestamp_future,timezone.utc)
aspects_future=get_aspects(date_future)
for planet_a,aspects in aspects_future.items():
try:
pre_transits[planet_a]
except KeyError:
if not aspects_contains(aspects_now,planet_a):
pre_planet_a(planet_a,aspects,date_future,'entering')
continue
for aspect, planets in aspects.items():
try:
pre_transits[planet_a][aspect]
except KeyError:
if not aspects_contains(aspects_now,planet_a,aspect):
pre_aspect(planet_a,aspect,planets,date_future,'entering')
continue
for planet_b in planets:
try:
pre_transits[planet_a][aspect][planet_b]
except KeyError:
if not aspects_contains(aspects_now,planet_a,aspect,planet_b):
pre_planet_b(planet_a,aspect,planet_b,date_future,'entering')
post_transits={}
for planet_a,aspects in pre_transits.items():
post_transits[planet_a]={}
for aspect,planets in aspects.items():
for planet_b,transit in planets.items():
direction=transit['direction']
date=transit['date']
if direction not in post_transits[planet_a]:
post_transits[planet_a][direction]={}
if date not in post_transits[planet_a][direction]:
post_transits[planet_a][direction][date]={}
if aspect not in post_transits[planet_a][direction][date]:
post_transits[planet_a][direction][date][aspect]=[]
post_transits[planet_a][direction][date][aspect].append(planet_b)
return post_transits
@cache
def get_aspect_transits_formatted(date=today(),minor=False,maxdays=28):
aspect_transits=get_aspect_transits(date,minor,maxdays)
aspect_transits_formatted=''
for planet_a,directions in aspect_transits.items():
aspect_transits_formatted+=planet_a+' is '
direction_list=[]
for direction,dates in directions.items():
date_list=[]
for date,aspects in dates.items():
aspect_list=[]
for aspect,planets in aspects.items():
aspect_list.append(aspect+' with '+get_list_formatted(planets))
date_list.append(get_list_formatted(aspect_list)+' on '+date.strftime(date_format))
direction_list.append(direction+' '+get_list_formatted(date_list))
aspect_transits_formatted+=get_list_formatted(direction_list)+'\n'
return aspect_transits_formatted
# Twitch functions # Twitch functions
@@ -397,11 +169,7 @@ def login(*args):
global device_code global device_code
if device_code==None: if device_code==None:
print('astrolobot is still starting, please wait',sys.stderr) print('astrolobot is still starting, please wait',sys.stderr)
else: webbrowser.open('https://www.twitch.tv/activate?public=true&device-code='+device_code)
login_url='https://www.twitch.tv/activate?public=true&device-code='+device_code
print("if your browser doesn't automatically open, go to the following url:")
print(login_url)
webbrowser.open(login_url)
def save_tokens(access_token,refresh_token): def save_tokens(access_token,refresh_token):
with open(script_path()+'tokens.json','w') as file: with open(script_path()+'tokens.json','w') as file:
@@ -470,7 +238,7 @@ def main():
async def on_code(code: str): async def on_code(code: str):
global device_code, obs_settings global device_code, obs_settings
device_code=code device_code=code
if len(tokens)==0: if obs_settings == None:
login() login()
@client.event @client.event
@@ -490,29 +258,12 @@ def main():
async def on_chat_message(data: eventsub.chat.MessageEvent): async def on_chat_message(data: eventsub.chat.MessageEvent):
config=load_settings() config=load_settings()
if data['message']['text']==config['positions']: if data['message']['text']==config['positions']:
for line in get_positions_formatted().splitlines(False): for line in get_planet_info_formatted().splitlines(False):
await client.channel.chat.send_message(line,data['message_id']) await client.channel.chat.send_message(line,data['message_id'])
return return
if data['message']['text']==config['transits']: if data['message']['text']==config['transits']:
for line in get_transits_formatted().splitlines(False): for line in get_transits_formatted().splitlines(False):
await client.channel.chat.send_message(line,data['message_id']) await client.channel.chat.send_message(line,data['message_id'])
return
if data['message']['text']==config['major_aspects']:
for line in get_aspects_formatted().splitlines(False):
await client.channel.chat.send_message(line,data['message_id'])
return
if data['message']['text']==config['minor_aspects']:
for line in get_aspects_formatted(minor=True).splitlines(False):
await client.channel.chat.send_message(line,data['message_id'])
return
if data['message']['text']==config['major_aspect_transits']:
for line in get_aspect_transits_formatted().splitlines(False):
await client.channel.chat.send_message(line,data['message_id'])
return
if data['message']['text']==config['minor_aspect_transits']:
for line in get_aspect_transits_formatted(minor=True).splitlines(False):
await client.channel.chat.send_message(line,data['message_id'])
return
client.run(*tokens.values()) client.run(*tokens.values())
@@ -524,9 +275,6 @@ def script_main():
dependency_setup() dependency_setup()
main() main()
def obs_update(*args):
Thread(target=update,daemon=True).start()
def obs_load_settings(): def obs_load_settings():
import obspython import obspython
global obs_settings global obs_settings
@@ -561,14 +309,10 @@ def script_properties():
import obspython import obspython
properties=obspython.obs_properties_create() properties=obspython.obs_properties_create()
obspython.obs_properties_add_button(properties,'login','Login to Twitch',login) obspython.obs_properties_add_button(properties,'login','Login to Twitch',login)
obspython.obs_properties_add_button(properties,'update','Check for Updates',obs_update) obspython.obs_properties_add_button(properties,'update','Check for Updates',update)
obspython.obs_properties_add_text(properties,'commands','commands',obspython.OBS_TEXT_INFO) obspython.obs_properties_add_text(properties,'commands','commands',obspython.OBS_TEXT_INFO)
obspython.obs_properties_add_text(properties,'positions','positions',obspython.OBS_TEXT_DEFAULT) obspython.obs_properties_add_text(properties,'positions','positions',obspython.OBS_TEXT_DEFAULT)
obspython.obs_properties_add_text(properties,'transits','transits',obspython.OBS_TEXT_DEFAULT) obspython.obs_properties_add_text(properties,'transits','transits',obspython.OBS_TEXT_DEFAULT)
obspython.obs_properties_add_text(properties,'major_aspects','major aspects',obspython.OBS_TEXT_DEFAULT)
obspython.obs_properties_add_text(properties,'minor_aspects','minor aspects',obspython.OBS_TEXT_DEFAULT)
obspython.obs_properties_add_text(properties,'major_aspect_transits','major aspect transits',obspython.OBS_TEXT_DEFAULT)
obspython.obs_properties_add_text(properties,'minor_aspect_transits','minor aspect transits',obspython.OBS_TEXT_DEFAULT)
return properties return properties
def script_defaults(settings): def script_defaults(settings):
@@ -576,10 +320,6 @@ def script_defaults(settings):
obspython.obs_data_set_default_string(settings,'commands','you can change the commands below, they save automatically') obspython.obs_data_set_default_string(settings,'commands','you can change the commands below, they save automatically')
obspython.obs_data_set_default_string(settings,'positions','!positions') obspython.obs_data_set_default_string(settings,'positions','!positions')
obspython.obs_data_set_default_string(settings,'transits','!transits') obspython.obs_data_set_default_string(settings,'transits','!transits')
obspython.obs_data_set_default_string(settings,'major_aspects','!aspects major')
obspython.obs_data_set_default_string(settings,'minor_aspects','!aspects minor')
obspython.obs_data_set_default_string(settings,'major_aspect_transits','!aspects major transits')
obspython.obs_data_set_default_string(settings,'minor_aspect_transits','!aspects minor transits')
def script_load(settings): def script_load(settings):
global obs_settings, save_tokens, load_tokens, load_settings global obs_settings, save_tokens, load_tokens, load_settings
@@ -610,10 +350,6 @@ if __name__ == '__main__':
def script_path(): def script_path():
return path.dirname(__file__)+'/' return path.dirname(__file__)+'/'
dependency_setup() dependency_setup()
print(get_positions_formatted()) print(get_planet_info_formatted())
print(get_transits_formatted()) print(get_transits_formatted())
print(get_aspects_formatted())
print(get_aspects_formatted(minor=True))
print(get_aspect_transits_formatted())
print(get_aspect_transits_formatted(minor=True))
main() main()
+1 -1
View File
@@ -1,2 +1,2 @@
pyswisseph pyswisseph
twitch.py == 3.* twitch.py