@@ -1,11 +1,14 @@
from datetime import datetime
import json
import os
def readfile ( filename ) :
with open ( module_path + filename ) as f :
return f . read ( )
def writefile ( filename , data ) :
with open ( module_path + filename , ' w ' ) as f :
f . write ( data )
# global constants
# Accusative - znahidnyj
@@ -28,6 +31,88 @@ lesson_types_to_strings = {
# global variables
module_path = " "
def get_preference_by_id ( user_id , name ) :
if not os . path . exists ( module_path + f " preference-db/ { user_id } .json " ) :
return None
raw_prefs = readfile ( f " preference-db/ { user_id } .json " )
try :
preferences = json . loads ( raw_prefs )
except Exception as e :
return None
if not name in preferences :
return None
return preferences [ name ]
def get_all_preferences_by_id ( user_id ) :
user_preferences = {
" output-style " : " legacy-vibrant " ,
" output-style-lesson " : " None " ,
" output-style-lookup " : " None "
}
# label defaults as defaults and let custom settings override these labels
for i in user_preferences :
user_preferences [ i ] + = " <i>(default)</i> "
for i in user_preferences :
override = get_preference_by_id ( user_id , i )
if override != None :
user_preferences [ i ] = override
return user_preferences
def set_preference_by_id ( user_id , name , value ) :
if not os . path . exists ( module_path + " preference-db/ " ) :
os . mkdir ( module_path + " preference-db/ " )
preferences = { }
if os . path . exists ( module_path + f " preference-db/ { user_id } .json " ) :
try :
raw_prefs = readfile ( f " preference-db/ { user_id } .json " )
preferences = json . loads ( raw_prefs )
except Exception as e :
preferences = { }
else :
preferences = { }
preferences [ name ] = value
final_data = json . dumps ( preferences )
writefile ( f " preference-db/ { user_id } .json " , final_data )
def clear_preference_by_id ( user_id , name ) :
if not os . path . exists ( module_path + " preference-db/ " ) :
os . mkdir ( module_path + " preference-db/ " )
preferences = { }
if os . path . exists ( module_path + f " preference-db/ { user_id } .json " ) :
try :
raw_prefs = readfile ( f " preference-db/ { user_id } .json " )
preferences = json . loads ( raw_prefs )
except Exception as e :
preferences = { }
else :
preferences = { }
if name in preferences :
del preferences [ name ]
final_data = json . dumps ( preferences )
writefile ( f " preference-db/ { user_id } .json " , final_data )
def load_template ( template , part ) :
return readfile ( f " templates/ { template } / { part } .msg " )
def escaped_string_markdownV2 ( input_string ) :
result_string = input_string
@@ -78,30 +163,63 @@ def get_name_of_lesson_type(lesson_type):
def generate_lesson_description ( lesson , start_datetime , end_datetime , current_day , current_week , overrides = { } ,
custom_name_prefix = " <b> Назва</b> " ) :
output_settings = { " name " : True , " date " : True , " teacher " : True , " link " : True , " comment " : True }
output_settings. update ( overrides )
custom_name_prefix = " Назва ", template = " legacy-vibrant " , force_date_at_top = False ) :
# temporarily not supported
# output_settings = {"name": True, "date": True, "teacher": True, "link": True, "comment": True}
#output_settings.update(overrides)
result = " "
if output_settings [ ' name ' ] :
result + = f " { custom_name_prefix } : { escaped_string_html ( lesson [ ' name ' ] ) } ( { escaped_string_html ( get_name_of_lesson_type ( lesson [ ' type ' ] ) ) } ) \n "
if output_settings [ ' date ' ] :
if lesson . __class__ == dict :
if force_date_at_top :
total_result = load_template ( template , " date " )
human_readable_date = get_human_readable_date ( start_datetime , end_datetime ,
current_day , current_week )
result + = f " <b>Дата</b>: { escaped_string_html ( human_readable_date ) } \n "
total_ result = total_result . replace ( " % DATE % " , human_readable_date )
if output_settings [ ' teacher ' ] :
result + = f " <b>Викладач</b>: { escaped_string_html ( lesson [ ' teacher ' ] ) } \n "
total_result + = load_template ( template , " multiple " )
for i in [ ' name ' , ' teacher ' , ' link ' ] :
total_result = total_result . replace ( f " % { i . upper ( ) } % " , lesson [ i ] )
if output_settings [ ' link ' ] :
result + = f " <b>Посилання</b>: { escaped_string_html ( lesson [ ' link ' ] ) } \n "
total_result = total_result . replace ( " % DATE % " , human_readable_date )
total_result = total_result . replace ( " % TYPE % " , get_name_of_lesson_type ( lesson [ ' type ' ] ) )
total_result = total_result . replace ( " % NAME_PREFIX % " , custom_name_prefix )
if output_settings [ ' comment ' ] and ' comment ' in lesson :
result + = f " <b>Примітка</b>: { escaped_string_html ( lesson [ ' comment ' ] ) } \n "
return total_result + " \n "
return result
else :
active_template = load_template ( template , " single " )
for i in [ ' name ' , ' teacher ' , ' link ' ] :
active_template = active_template . replace ( f " % { i . upper ( ) } % " , lesson [ i ] )
human_readable_date = get_human_readable_date ( start_datetime , end_datetime ,
current_day , current_week )
active_template = active_template . replace ( " % DATE % " , human_readable_date )
active_template = active_template . replace ( " % TYPE % " , get_name_of_lesson_type ( lesson [ ' type ' ] ) )
active_template = active_template . replace ( " % NAME_PREFIX % " , custom_name_prefix )
return active_template
elif lesson . __class__ == list :
total_result = load_template ( template , " date " )
human_readable_date = get_human_readable_date ( start_datetime , end_datetime ,
current_day , current_week )
total_result = total_result . replace ( " % DATE % " , human_readable_date )
for l in lesson :
active_template = load_template ( template , " multiple " )
for i in [ ' name ' , ' teacher ' , ' link ' ] :
active_template = active_template . replace ( f " % { i . upper ( ) } % " , l [ i ] )
active_template = active_template . replace ( " % DATE % " , human_readable_date )
active_template = active_template . replace ( " % TYPE % " , get_name_of_lesson_type ( l [ ' type ' ] ) )
active_template = active_template . replace ( " % NAME_PREFIX % " , custom_name_prefix )
total_result + = active_template + " \n "
return total_result
def get_schedule_data_from ( filename ) :
@@ -151,45 +269,15 @@ def process_arguments(args, base_day):
def get_lesson_description ( schedule , reference_time , lesson_time , current_day , current_week , overrides = { } ,
custom_name_prefix = " <b> Назва</b> " , force_date_at_top = False ) :
custom_name_prefix = " Назва" , template = " legacy-vibrant " , force_date_at_top = False ) :
lesson_record = schedule [ lesson_time ]
lesson_start_datetime = datetime . fromtimestamp ( reference_time + lesson_time )
lesson_end_datetime = datetime . fromtimestamp ( reference_time + lesson_time + 5400 )
if lesson_record . __class__ == dict :
if force_date_at_top :
user_defined_overrides = dict ( overrides )
internal_overrides = dict ( overrides )
internal_overrides [ ' date ' ] = False
description = generate_lesson_description ( lesson_record , lesson_start_datetime , lesson_end_datetime , current_day ,
current_week , overrides = internal_overrides )
if ' date ' in user_defined_overrides and not user_defined_overrides [ ' date ' ] :
return description
else :
human_readable_date = get_human_readable_date ( lesson_start_datetime , lesson_end_datetime ,
current_day , current_week )
return f " <b><u> { human_readable_date . capitalize ( ) } </u></b>: \n " + description
else :
return generate_lesson_description ( lesson_record , lesson_start_datetime , lesson_end_datetime , current_day ,
current_week , overrides = overrides )
elif lesson_record . __class__ == list :
user_defined_overrides = dict ( overrides )
internal_overrides = dict ( overrides )
internal_overrides [ ' date ' ] = False
descriptions = [ generate_lesson_description ( i , lesson_start_datetime , lesson_end_datetime , current_day ,
current_week , overrides = internal_overrides , custom_name_prefix = custom_name_prefix ) for i in lesson_record ]
if ' date ' in user_defined_overrides and not user_defined_overrides [ ' date ' ] :
return " \n " . join ( descriptions )
else :
human_readable_date = get_human_readable_date ( lesson_start_datetime , lesson_end_datetime ,
current_day , current_week )
return f " <b><u> { human_readable_date . capitalize ( ) } </u></b>: \n " + " \n " . join ( descriptions )
current_week , overrides = overrides , custom_name_prefix = custom_name_prefix , template = template ,
force_date_at_top = force_date_at_top )
def process ( message , path ) :
@@ -201,7 +289,7 @@ def process(message, path):
# one printable symbol, so this is already protected
base_command = full_command [ 0 ] . lower ( )
if base_command not in [ " !пара " , " !пари " ] :
if base_command not in [ " !пара " , " !пари " , " !schedule-ctl " ]:
return None , None
global module_path
@@ -221,16 +309,18 @@ def process(message, path):
reference_time = int ( current_time . strftime ( " %s " ) ) - current_seconds
if base_command == " !пара " :
# easter egg
study_begin_ts = int ( datetime ( year = 2023 , month = 9 , day = 4 ) . strftime ( " %s " ) )
current_ts = int ( datetime . now ( ) . strftime ( " %s " ) )
if - 3600 * 4 < study_begin_ts - current_ts < 0 :
return " Навчання незабаром розпочнеться! " , None
return " Навчання от-от розпочнеться! " , None
elif 0 < = study_begin_ts - current_ts < 1209600 :
return f " До навчання залишилося { study_begin_ts - current_ts } секунд... " , None
elif study_begin_ts - current_ts > = 1209600 :
return " Ви маєте законне право відпочити, пари почнуться не скоро " , None
# actual lesson finding code
upcoming_lessons = [ timestamp for timestamp in schedule if timestamp > current_seconds - 5400 ]
if len ( upcoming_lessons ) > 0 :
@@ -238,8 +328,34 @@ def process(message, path):
else :
closest_lesson_time = min ( schedule )
# shifting lesson pointer if requested to do so
if len ( full_command ) > = 2 :
possible_times = list ( schedule . keys ( ) )
current_pointer_position = possible_times . index ( closest_lesson_time )
total_list_length = len ( possible_times )
if len ( full_command [ 1 ] ) > 1 and full_command [ 1 ] [ 1 : ] . isdigit ( ) :
if full_command [ 1 ] [ 0 ] == " + " :
current_pointer_position = ( current_pointer_position + int ( full_command [ 1 ] [ 1 : ] ) ) % total_list_length
else :
current_pointer_position = ( current_pointer_position - int ( full_command [ 1 ] [ 1 : ] ) ) % total_list_length
closest_lesson_time = possible_times [ current_pointer_position ]
# getting corrent style
output_style_preference = " legacy-vibrant "
general_output_style_preference = get_preference_by_id ( message . from_user . id , " output-style " )
if general_output_style_preference != None :
output_style_preference = general_output_style_preference
specific_output_style_preference = get_preference_by_id ( message . from_user . id , " output-style-lesson " )
if specific_output_style_preference != None :
output_style_preference = specific_output_style_preference
# returning generated pair description
return get_lesson_description ( schedule , reference_time , closest_lesson_time , current_day ,
current_week , custom_name_prefix = " <b> Актуальна пара</b> " ) , " HTML "
current_week , custom_name_prefix = " Актуальна пара ", template = output_style_preference ) , " HTML "
elif base_command == " !пари " :
base_day = current_week * 7 + current_day
@@ -253,8 +369,52 @@ def process(message, path):
lesson_list = [ i for i in schedule if selected_day * 86400 < = i < ( selected_day + 1 ) * 86400 ]
output_style_preference = " legacy-vibrant "
general_output_style_preference = get_preference_by_id ( message . from_user . id , " output-style " )
if general_output_style_preference != None :
output_style_preference = general_output_style_preference
specific_output_style_preference = get_preference_by_id ( message . from_user . id , " output-style-lookup " )
if specific_output_style_preference != None :
output_style_preference = specific_output_style_preference
lesson_descriptions_list = [ get_lesson_description ( schedule , reference_time , lesson_time , current_day ,
current_week , overrides = preferences , custom_name_prefix = " <b> Назва</b> " , force_date_at_top = True )
current_week , overrides = preferences , custom_name_prefix = " Назва ", template = output_style_preference , force_date_at_top = True )
for lesson_time in lesson_list ]
return f " <b><u>Пари у { WEEKDAYS_ACCUSATIVE [ selected_day % 7 ] } </u></b>: \n \n \n " + " \n \n " . join ( lesson_descriptions_list ) , " HTML "
return f " <b><u>Пари у { WEEKDAYS_ACCUSATIVE [ selected_day % 7 ] } </u></b>: \n \n \n " + " \n " . join ( lesson_descriptions_list ) , " HTML "
elif base_command == " !schedule-ctl " and len ( full_command ) > = 2 :
if full_command [ 1 ] == " list " :
prefs = get_all_preferences_by_id ( message . from_user . id )
return " Ваші персональні налаштування: \n " + ' \n ' . join ( [ f " - { k } = { v } " for k , v in prefs . items ( ) ] ) , " HTML "
elif full_command [ 1 ] == " set " and len ( full_command ) == 4 :
prefs = get_all_preferences_by_id ( message . from_user . id )
if full_command [ 2 ] in prefs :
if full_command [ 2 ] in [ " output-style " , " output-style-lesson " , " output-style-lookup " ] :
if full_command [ 3 ] not in os . listdir ( module_path + " templates/ " ) :
return f " Стилю { full_command [ 3 ] } не існує; доступні варіанти: " \
+ ' , ' . join ( os . listdir ( module_path + " templates/ " ) ) , " HTML "
previous_value = prefs [ full_command [ 2 ] ]
prefs [ full_command [ 2 ] ] = full_command [ 3 ]
set_preference_by_id ( message . from_user . id , full_command [ 2 ] , full_command [ 3 ] )
return f " Змінено значення { full_command [ 2 ] } : { previous_value } -> { full_command [ 3 ] } " , " HTML "
else :
return f " Такого налаштування не існує; переглянути наявні налаштування можна за допомогою команди <u>!schedule-ctl list</u> " , " HTML "
elif full_command [ 1 ] == " get " and len ( full_command ) == 3 :
requested_preference = get_preference_by_id ( message . from_user . id , full_command [ 2 ] )
return f " Налаштування { full_command [ 2 ] } має значення { requested_preference } " , " HTML "
elif full_command [ 1 ] == " clear " and len ( full_command ) == 3 :
clear_preference_by_id ( message . from_user . id , full_command [ 2 ] )
return f " Очищено значення для налаштування { full_command [ 2 ] } , надалі для нього використовуватиметься стандартне значення " , " HTML "
else :
return " Такої команди не існує (а б о був використаний помилковий синтаксис) " , " HTML "