From a06c15cd42b7ba07bee539fa38fc4bf92c4854d5 Mon Sep 17 00:00:00 2001 From: PokestarFan Date: Tue, 19 Sep 2017 20:21:02 -0400 Subject: [PATCH 1/6] Add logger level customization --- modules/logger.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/logger.py b/modules/logger.py index 2e89e2e..102cd48 100644 --- a/modules/logger.py +++ b/modules/logger.py @@ -3,13 +3,13 @@ import sys -def setup_logger(name): +def setup_logger(name, loglevel=logging.INFO): file_handler = logging.FileHandler(filename='logs/{}_{}.log'.format(name, datetime.now().strftime('%m_%d_%y'))) stdout_handler = logging.StreamHandler(sys.stdout) handlers = [file_handler, stdout_handler] logging.basicConfig( - level=logging.INFO, + level=loglevel, format='[%(asctime)s] {%(filename)s:%(lineno)d} (%(funcName)s) %(levelname)s - %(message)s', handlers=handlers ) From 5f8124cbaaf3b613701f52676543234a6623e876 Mon Sep 17 00:00:00 2001 From: PokestarFan Date: Wed, 20 Sep 2017 21:33:52 -0400 Subject: [PATCH 2/6] Major code overhaul --- duplicate.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/duplicate.py b/duplicate.py index 2752868..a277303 100644 --- a/duplicate.py +++ b/duplicate.py @@ -8,8 +8,8 @@ logger = setup_logger('duplicates') -def action(): - for sub_id in reddit.subreddit('all').stream.submissions(): +def run_bot(sub_id): + if True: try: logging.debug('Starting submission {}'.format(sub_id)) blockeduser = 0 @@ -62,7 +62,11 @@ def action(): raise KeyboardInterrupt except: logger.error('Error on submission {} occured.'.format(str(sub_id)), exc_info=True) - + + +def action(): + for sub_id in reddit.subreddit('all').stream.submissions(): + run_bot(sub_id) if __name__ == '__main__': From c7ad3232ff13e088f6f3852c8676f1c6f8561011 Mon Sep 17 00:00:00 2001 From: PokestarFan Date: Thu, 18 Jan 2018 18:15:52 -0500 Subject: [PATCH 3/6] Add --- duplicate.py | 158 +++++++++++++++++++++++++++++----------------- entriesadder.py | 44 +++++++++++++ modules/footer.py | 1 - modules/table.py | 4 ++ 4 files changed, 148 insertions(+), 59 deletions(-) create mode 100644 entriesadder.py delete mode 100644 modules/footer.py create mode 100644 modules/table.py diff --git a/duplicate.py b/duplicate.py index a277303..0b9b047 100644 --- a/duplicate.py +++ b/duplicate.py @@ -1,79 +1,121 @@ -from modules.logger import setup_logger import logging from datetime import datetime + import praw import prawcore +from pokestarfansloggingsetup import setup_logger + from modules.login import reddit -from modules.footer import footer +from modules.table import starter logger = setup_logger('duplicates') + +# noinspection PyBroadException +def generate_and_reply(submission): + footer = '\n\n----\n\n ^^I ^^am ^^a ^^bot ^^[FAQ](https://www.reddit.com/r/DuplicatesBot/wiki/index)-[' \ + 'Code](https://github.com/PokestarFan/DuplicateBot)-[Bugs](' \ + 'https://www.reddit.com/r/DuplicatesBot/comments/6ypgmx/bugs_and_problems/)-[Suggestions](' \ + 'https://www.reddit.com/r/DuplicatesBot/comments/6ypg85/suggestion_for_duplicatesbot/)-[Block ' \ + 'user (op only)' \ + '](' \ + 'https://www.reddit.com/message/compose/?to=DuplicatesBotBlocker&subject=remove%20user&message' \ + '={user})-[Block from subreddit (mods only)](' \ + 'https://www.reddit.com/message/compose/?to=DuplicatesBotBlocker&subject=remove%20subreddit' \ + '&message={sub})\n' \ + '\n^^Now ^^you ^^can ^^remove ^^the ^^comment ^^by ^^replying ^^delete! '.format(user= + str( + submission.author), sub=str(submission.subreddit)) + global message + sub_id = submission.subreddit + for dup_sub in submission.duplicates(): + duplicates = [] + time = dup_sub.created + time = str(datetime.fromtimestamp(time)) + author = '/u/' + str(dup_sub.author) + if str(submission.author) == author: + author = author + ' [author of both threads]' + duplicates.append(['[{}]({})'.format(str(dup_sub.title), 'https://www.reddit.com' + str(dup_sub.permalink)), + str(dup_sub.subreddit), author, str(time), str(dup_sub.score)]) + if len(duplicates) > 0: + message = 'Here is a list of threads in other subreddits about the same content:\n' + for dup in duplicates: + starter.add_row_with_list(dup) + message += '\n' + starter.table + message += '\n' + footer + try: + submission.reply(message) + logger.info('Message posted on {}'.format(sub_id)) + logger.debug('Message: {}'.format(message)) + message = '' + except praw.exceptions.APIException: + logger.debug('Submission {} has been skipped due to missing text.'.format(sub_id)) + message = '' + except prawcore.exceptions.Forbidden: + logger.debug('You are blocked on /r/{}'.format(str(submission.subreddit))) + message = '' + except AssertionError: + logger.debug('Assertion Error occured! Printing message and traceback.') + logger.debug(message + str(len(message)), exc_info=True) + message = '' + except(KeyboardInterrupt, SystemExit): + raise + except Exception: + logger.error('Error occurred!', exc_info=True) + message = '' + except: + logger.critical( + 'Massive Error occurred! Not part of the Exception, KeyboardInterrupt or SystemExit exceptions. Fix ASAP.', + exc_info=True) + message = '' + + def run_bot(sub_id): + global blocked_sub if True: try: - logging.debug('Starting submission {}'.format(sub_id)) - blockeduser = 0 - duplicates = [] - submission = praw.models.Submission(reddit, id = sub_id) - with open('blockusers.txt','r') as newfile: - for line in newfile.readlines(): - line = line.strip('\n') - if str(submission.author) == line or 'bot' in str(submission.author).lower(): - blockeduser = 1 - logger.debug('User {}\'s submission {} was blocked from posting'.format(str(submission.author),str(sub_id))) - else: - pass - if blockeduser == 0: - for duplicate in submission.duplicates(): - dup_sub = praw.models.Submission(reddit, id = duplicate) - if 'imagesof' not in str(dup_sub.subreddit).lower() and 'auto' not in str(dup_sub.subreddit).lower() and 'bot' not in str(dup_sub.author).lower() and 'mistyfront' not in str(dup_sub.subreddit).lower() and 'unitedfederation' not in str(dup_sub.subreddit).lower(): - time = dup_sub.created - time = str(datetime.fromtimestamp(time)) - author = '/u/'+str(dup_sub.author) - if str(submission.author) == author: - author = author + ' [author of both threads]' - duplicates.append({'title':str(dup_sub.title), 'subreddit':str(dup_sub.subreddit), 'link':'https://www.reddit.com'+str(dup_sub.permalink), 'time':str(time), 'author':author, 'karma': str(dup_sub.score)}) - if len(duplicates) > 0: - message = 'Here is a list of threads in other subreddits about the same content:\n' - for dup in duplicates: - message = str(message + '\n * [{}]({}) on /r/{} with {} karma (created at {} by {})').format(dup['title'], dup['link'], dup['subreddit'], dup['karma'],dup['time'], dup['author']) - message = message + footer - try: - submission.reply(message) - logger.info('Message posted on {}'.format(sub_id)) - logger.debug('Message: {}'.format(message)) - message = '' - except(praw.exceptions.APIException, UnboundLocalError)as e: - logger.debug('Submission {} has been skipped due to missing text'.format(sub_id)) - message = '' - except(prawcore.exceptions.Forbidden): - logger.debug('You are blocked on /r/{}'.format(str(submission.subreddit))) - message = '' - except(AssertionError): - logger.debug('Assertion Error occured! Printing message and traceback.') - logger.debug(message + str(len(message)), exc_info=True) - message = '' - except(KeyboardInterrupt): - raise KeyboardInterrupt - except: - logger.error('Error occured!', exc_info=True) - message = '' - except(KeyboardInterrupt): - raise KeyboardInterrupt - except: - logger.error('Error on submission {} occured.'.format(str(sub_id)), exc_info=True) + logging.debug('Starting submission {}'.format(str(sub_id))) + blocked_user = 0 + blocked_sub = 0 + submission = sub_id + try: + with open('blockusers.txt', 'r') as new_file: + for line in new_file.readlines(): + line = line.strip('\n') + if str(submission.author).lower() == line.lower() or 'bot' in str(submission.author).lower(): + blocked_user = 1 + break + except FileNotFoundError: + with open('blockusers.txt', 'w'): + blocked_user = 0 + try: + with open('blockedsubs.txt', 'r') as new_file: + for line in new_file.readlines(): + line = line.strip('\n') + if str(submission.subreddit).lower() == line.lower(): + blocked_sub = 1 + break + except FileNotFoundError: + with open('blockedsubs.txt', 'w'): + blocked_sub = 0 + if blocked_user == 0 and blocked_sub == 0: + generate_and_reply(sub_id) + except(KeyboardInterrupt, SystemExit): + raise + except Exception: + logger.error('Error on submission {} occurred.'.format(str(sub_id)), exc_info=True) def action(): for sub_id in reddit.subreddit('all').stream.submissions(): run_bot(sub_id) - + if __name__ == '__main__': while True: try: action() - except(KeyboardInterrupt): - raise KeyboardInterrupt - except: - logger.critical('Error has occured when running main loop, please resolve asap', exc_info=True) \ No newline at end of file + except(KeyboardInterrupt, SystemExit): + raise + except Exception: + logger.critical('Error has occured when running main loop, please resolve asap', exc_info=True) diff --git a/entriesadder.py b/entriesadder.py new file mode 100644 index 0000000..921334c --- /dev/null +++ b/entriesadder.py @@ -0,0 +1,44 @@ +import praw +from pokestarfansloggingsetup import setup_logger +import logging +from modules.entrylogin import reddit + +logger = setup_logger('usersubblocker') + + +def write_to_user_file(text): + with open('blockusers.txt', 'a') as file: + file.write(text) + + +def write_to_sub_file(text): + with open('blockedsubs.txt', 'a') as file: + file.write(text) + + +def strip_message(message): + try: + if message.subject == 'remove subreddit': + subreddit = reddit.subreddit(message) + mod = False + for moderator in reddit.subreddit(subreddit).moderator(): + if str(message.author) == str(moderator): + mod = True + break + if mod: + write_to_sub_file(message.body) + else: + message.reply('You are not a moderator of the subreddit so your request has not been preformed.') + elif message.subject == 'remove user': + if str(message.author) == message.body: + write_to_user_file(message.body) + else: + message.reply('You are not the OP of the submission so your request has not been preformed.') + except Exception: + logging.warning('', exc_info=True) + + +def check_for_messages(reddit): + for message in reddit.inbox.unread(mark_read=True): + strip_message(message) + reddit.inbox.mark_read(message) diff --git a/modules/footer.py b/modules/footer.py deleted file mode 100644 index 5d6c938..0000000 --- a/modules/footer.py +++ /dev/null @@ -1 +0,0 @@ -footer = '\n\n----\n\n ^^I ^^am ^^a ^^bot ^^[FAQ](https://www.reddit.com/r/DuplicatesBot/wiki/index)-[Code](https://github.com/PokestarFan/DuplicateBot)-[Bugs](https://www.reddit.com/r/DuplicatesBot/comments/6ypgmx/bugs_and_problems/)-[Suggestions](https://www.reddit.com/r/DuplicatesBot/comments/6ypg85/suggestion_for_duplicatesbot/)-[Block](https://www.reddit.com/r/DuplicatesBot/wiki/index#wiki_block_bot_from_tagging_on_your_posts)\n\n^^Now ^^you ^^can ^^remove ^^the ^^comment ^^by ^^replying ^^delete!' \ No newline at end of file diff --git a/modules/table.py b/modules/table.py new file mode 100644 index 0000000..c2bb0ef --- /dev/null +++ b/modules/table.py @@ -0,0 +1,4 @@ +from markdowntable import Table as ta + +starter = ta('Title') +starter.all_columns('Subreddit','Author','Time','Karma') \ No newline at end of file From c907186debed424a7994cbfeb938b1c0bf89c9f1 Mon Sep 17 00:00:00 2001 From: PokestarFan Date: Thu, 18 Jan 2018 18:49:23 -0500 Subject: [PATCH 4/6] Finally done --- delete.py | 47 +++++++++++++++++++++++++---------------------- duplicate.py | 22 ++++++++++------------ entriesadder.py | 18 +++++++++++++----- 3 files changed, 48 insertions(+), 39 deletions(-) diff --git a/delete.py b/delete.py index 2f6658e..4eab928 100644 --- a/delete.py +++ b/delete.py @@ -1,30 +1,33 @@ -import praw -from modules.logger import setup_logger -from modules.login import reddit -from modules.footer import footer import logging import time +from modules.logger import setup_logger +from modules.login import reddit + logger = setup_logger('user_removed_comments') def main(): - try: - for item in reddit.inbox.stream(): - logger.debug('On item {}'.format(str(item))) - try: - if 'delete' in item.body.lower(): - item.parent().delete() - logging.info('Comment {} removed'.format(str(item.parent()))) - item.author.message('Removal of comment {}'.format(str(item.parent())),'The top level post has been removed.') - except: - logging.debug('Item {} skipped'.format(str(item))) - except(KeyboardInterrupt): - raise KeyboardInterrupt - except: - logging.error('Error!', exc_info=True) - main() - + try: + for item in reddit.inbox.stream(): + logger.debug('On item {}'.format(str(item))) + try: + if 'delete' in item.body.lower() and item.author == item.submission.author: + item.parent().delete() + logging.info('Comment {} removed'.format(str(item.parent()))) + item.author.message('Removal of comment {}'.format(str(item.parent())), + 'The top level post has been removed.') + except AttributeError: + pass + except: + logging.debug('Item {} skipped'.format(str(item))) + except(KeyboardInterrupt): + raise KeyboardInterrupt + except: + logging.error('Error!', exc_info=True) + main() + + while True: - main() - time.sleep(30) \ No newline at end of file + main() + time.sleep(30) diff --git a/duplicate.py b/duplicate.py index 0b9b047..f6ba10a 100644 --- a/duplicate.py +++ b/duplicate.py @@ -3,18 +3,20 @@ import praw import prawcore +from markdowntable import Table as ta from pokestarfansloggingsetup import setup_logger from modules.login import reddit -from modules.table import starter logger = setup_logger('duplicates') # noinspection PyBroadException def generate_and_reply(submission): - footer = '\n\n----\n\n ^^I ^^am ^^a ^^bot ^^[FAQ](https://www.reddit.com/r/DuplicatesBot/wiki/index)-[' \ - 'Code](https://github.com/PokestarFan/DuplicateBot)-[Bugs](' \ + starter = ta('Title') + starter.all_columns('Subreddit', 'Author', 'Time', 'Karma') + footer = '\n\n----\n\n I am a bot [FAQ](https://www.reddit.com/r/DuplicatesBot/wiki/index)-[' \ + 'Code](https://github.com/PokestarFan/DuplicateBot)-[Bugs](' \ 'https://www.reddit.com/r/DuplicatesBot/comments/6ypgmx/bugs_and_problems/)-[Suggestions](' \ 'https://www.reddit.com/r/DuplicatesBot/comments/6ypg85/suggestion_for_duplicatesbot/)-[Block ' \ 'user (op only)' \ @@ -23,7 +25,7 @@ def generate_and_reply(submission): '={user})-[Block from subreddit (mods only)](' \ 'https://www.reddit.com/message/compose/?to=DuplicatesBotBlocker&subject=remove%20subreddit' \ '&message={sub})\n' \ - '\n^^Now ^^you ^^can ^^remove ^^the ^^comment ^^by ^^replying ^^delete! '.format(user= + '\nNow you can remove the comment by replying delete! '.format(user= str( submission.author), sub=str(submission.subreddit)) global message @@ -36,7 +38,7 @@ def generate_and_reply(submission): if str(submission.author) == author: author = author + ' [author of both threads]' duplicates.append(['[{}]({})'.format(str(dup_sub.title), 'https://www.reddit.com' + str(dup_sub.permalink)), - str(dup_sub.subreddit), author, str(time), str(dup_sub.score)]) + '/r/' + str(dup_sub.subreddit), author, str(time), str(dup_sub.score)]) if len(duplicates) > 0: message = 'Here is a list of threads in other subreddits about the same content:\n' for dup in duplicates: @@ -45,28 +47,24 @@ def generate_and_reply(submission): message += '\n' + footer try: submission.reply(message) - logger.info('Message posted on {}'.format(sub_id)) + logger.info('Message posted on {}'.format(str(submission))) logger.debug('Message: {}'.format(message)) - message = '' - except praw.exceptions.APIException: + except(praw.exceptions.APIException, UnboundLocalError): logger.debug('Submission {} has been skipped due to missing text.'.format(sub_id)) - message = '' except prawcore.exceptions.Forbidden: logger.debug('You are blocked on /r/{}'.format(str(submission.subreddit))) - message = '' except AssertionError: logger.debug('Assertion Error occured! Printing message and traceback.') logger.debug(message + str(len(message)), exc_info=True) - message = '' except(KeyboardInterrupt, SystemExit): raise except Exception: logger.error('Error occurred!', exc_info=True) - message = '' except: logger.critical( 'Massive Error occurred! Not part of the Exception, KeyboardInterrupt or SystemExit exceptions. Fix ASAP.', exc_info=True) + finally: message = '' diff --git a/entriesadder.py b/entriesadder.py index 921334c..21fa0c1 100644 --- a/entriesadder.py +++ b/entriesadder.py @@ -1,6 +1,7 @@ -import praw -from pokestarfansloggingsetup import setup_logger import logging + +from pokestarfansloggingsetup import setup_logger + from modules.entrylogin import reddit logger = setup_logger('usersubblocker') @@ -39,6 +40,13 @@ def strip_message(message): def check_for_messages(reddit): - for message in reddit.inbox.unread(mark_read=True): - strip_message(message) - reddit.inbox.mark_read(message) + try: + for message in reddit.inbox.unread(mark_read=True): + strip_message(message) + reddit.inbox.mark_read(message) + except Exception: + logging.error('error!', exc_info=True) + + +while True: + check_for_messages(reddit) From 4cc8275c166512e60d445fc2d5ab3a04240d740a Mon Sep 17 00:00:00 2001 From: PokestarFan Date: Mon, 14 May 2018 15:39:24 -0400 Subject: [PATCH 5/6] Update duplicate.py --- duplicate.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/duplicate.py b/duplicate.py index f6ba10a..abc8d7a 100644 --- a/duplicate.py +++ b/duplicate.py @@ -1,3 +1,28 @@ +# +# +# PPPPPPPPPPPPPPPPP kkkkkkkk tttt FFFFFFFFFFFFFFFFFFFFFF +# P::::::::::::::::P k::::::k ttt:::t F::::::::::::::::::::F +# P::::::PPPPPP:::::P k::::::k t:::::t F::::::::::::::::::::F +# PP:::::P P:::::P k::::::k t:::::t FF::::::FFFFFFFFF::::F +# P::::P P:::::P ooooooooooo k:::::k kkkkkkk eeeeeeeeeeee ssssssssss ttttttt:::::ttttttt aaaaaaaaaaaaa rrrrr rrrrrrrrr F:::::F FFFFFFaaaaaaaaaaaaa nnnn nnnnnnnn +# P::::P P:::::Poo:::::::::::oo k:::::k k:::::kee::::::::::::ee ss::::::::::s t:::::::::::::::::t a::::::::::::a r::::rrr:::::::::r F:::::F a::::::::::::a n:::nn::::::::nn +# P::::PPPPPP:::::Po:::::::::::::::o k:::::k k:::::ke::::::eeeee:::::eess:::::::::::::s t:::::::::::::::::t aaaaaaaaa:::::ar:::::::::::::::::r F::::::FFFFFFFFFF aaaaaaaaa:::::an::::::::::::::nn +# P:::::::::::::PP o:::::ooooo:::::o k:::::k k:::::ke::::::e e:::::es::::::ssss:::::stttttt:::::::tttttt a::::arr::::::rrrrr::::::rF:::::::::::::::F a::::ann:::::::::::::::n +# P::::PPPPPPPPP o::::o o::::o k::::::k:::::k e:::::::eeeee::::::e s:::::s ssssss t:::::t aaaaaaa:::::a r:::::r r:::::rF:::::::::::::::F aaaaaaa:::::a n:::::nnnn:::::n +# P::::P o::::o o::::o k:::::::::::k e:::::::::::::::::e s::::::s t:::::t aa::::::::::::a r:::::r rrrrrrrF::::::FFFFFFFFFF aa::::::::::::a n::::n n::::n +# P::::P o::::o o::::o k:::::::::::k e::::::eeeeeeeeeee s::::::s t:::::t a::::aaaa::::::a r:::::r F:::::F a::::aaaa::::::a n::::n n::::n +# P::::P o::::o o::::o k::::::k:::::k e:::::::e ssssss s:::::s t:::::t tttttta::::a a:::::a r:::::r F:::::F a::::a a:::::a n::::n n::::n +# PP::::::PP o:::::ooooo:::::ok::::::k k:::::ke::::::::e s:::::ssss::::::s t::::::tttt:::::ta::::a a:::::a r:::::r FF:::::::FF a::::a a:::::a n::::n n::::n +# P::::::::P o:::::::::::::::ok::::::k k:::::ke::::::::eeeeeeee s::::::::::::::s tt::::::::::::::ta:::::aaaa::::::a r:::::r F::::::::FF a:::::aaaa::::::a n::::n n::::n +# P::::::::P oo:::::::::::oo k::::::k k:::::kee:::::::::::::e s:::::::::::ss tt:::::::::::tt a::::::::::aa:::ar:::::r F::::::::FF a::::::::::aa:::a n::::n n::::n +# PPPPPPPPPP ooooooooooo kkkkkkkk kkkkkkk eeeeeeeeeeeeee sssssssssss ttttttttttt aaaaaaaaaa aaaarrrrrrr FFFFFFFFFFF aaaaaaaaaa aaaa nnnnnn nnnnnn +# +# +# +# +# +# +# import logging from datetime import datetime From 86b5c7a4cc2122f991ba1b2e81a2c16725efe1cc Mon Sep 17 00:00:00 2001 From: PokestarFan Date: Mon, 14 May 2018 15:40:26 -0400 Subject: [PATCH 6/6] Update duplicate.py --- duplicate.py | 82 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/duplicate.py b/duplicate.py index abc8d7a..5c6ebe8 100644 --- a/duplicate.py +++ b/duplicate.py @@ -1,28 +1,60 @@ -# -# -# PPPPPPPPPPPPPPPPP kkkkkkkk tttt FFFFFFFFFFFFFFFFFFFFFF -# P::::::::::::::::P k::::::k ttt:::t F::::::::::::::::::::F -# P::::::PPPPPP:::::P k::::::k t:::::t F::::::::::::::::::::F -# PP:::::P P:::::P k::::::k t:::::t FF::::::FFFFFFFFF::::F -# P::::P P:::::P ooooooooooo k:::::k kkkkkkk eeeeeeeeeeee ssssssssss ttttttt:::::ttttttt aaaaaaaaaaaaa rrrrr rrrrrrrrr F:::::F FFFFFFaaaaaaaaaaaaa nnnn nnnnnnnn -# P::::P P:::::Poo:::::::::::oo k:::::k k:::::kee::::::::::::ee ss::::::::::s t:::::::::::::::::t a::::::::::::a r::::rrr:::::::::r F:::::F a::::::::::::a n:::nn::::::::nn -# P::::PPPPPP:::::Po:::::::::::::::o k:::::k k:::::ke::::::eeeee:::::eess:::::::::::::s t:::::::::::::::::t aaaaaaaaa:::::ar:::::::::::::::::r F::::::FFFFFFFFFF aaaaaaaaa:::::an::::::::::::::nn -# P:::::::::::::PP o:::::ooooo:::::o k:::::k k:::::ke::::::e e:::::es::::::ssss:::::stttttt:::::::tttttt a::::arr::::::rrrrr::::::rF:::::::::::::::F a::::ann:::::::::::::::n -# P::::PPPPPPPPP o::::o o::::o k::::::k:::::k e:::::::eeeee::::::e s:::::s ssssss t:::::t aaaaaaa:::::a r:::::r r:::::rF:::::::::::::::F aaaaaaa:::::a n:::::nnnn:::::n -# P::::P o::::o o::::o k:::::::::::k e:::::::::::::::::e s::::::s t:::::t aa::::::::::::a r:::::r rrrrrrrF::::::FFFFFFFFFF aa::::::::::::a n::::n n::::n -# P::::P o::::o o::::o k:::::::::::k e::::::eeeeeeeeeee s::::::s t:::::t a::::aaaa::::::a r:::::r F:::::F a::::aaaa::::::a n::::n n::::n -# P::::P o::::o o::::o k::::::k:::::k e:::::::e ssssss s:::::s t:::::t tttttta::::a a:::::a r:::::r F:::::F a::::a a:::::a n::::n n::::n -# PP::::::PP o:::::ooooo:::::ok::::::k k:::::ke::::::::e s:::::ssss::::::s t::::::tttt:::::ta::::a a:::::a r:::::r FF:::::::FF a::::a a:::::a n::::n n::::n -# P::::::::P o:::::::::::::::ok::::::k k:::::ke::::::::eeeeeeee s::::::::::::::s tt::::::::::::::ta:::::aaaa::::::a r:::::r F::::::::FF a:::::aaaa::::::a n::::n n::::n -# P::::::::P oo:::::::::::oo k::::::k k:::::kee:::::::::::::e s:::::::::::ss tt:::::::::::tt a::::::::::aa:::ar:::::r F::::::::FF a::::::::::aa:::a n::::n n::::n -# PPPPPPPPPP ooooooooooo kkkkkkkk kkkkkkk eeeeeeeeeeeeee sssssssssss ttttttttttt aaaaaaaaaa aaaarrrrrrr FFFFFFFFFFF aaaaaaaaaa aaaa nnnnnn nnnnnn -# -# -# -# -# -# -# +# +# +# PPPPPPPPPPPPPPPPP kkkkkkkk +# P::::::::::::::::P k::::::k +# P::::::PPPPPP:::::P k::::::k +# PP:::::P P:::::P k::::::k +# P::::P P:::::P ooooooooooo k:::::k kkkkkkk eeeeeeeeeeee +# P::::P P:::::Poo:::::::::::oo k:::::k k:::::kee::::::::::::ee +# P::::PPPPPP:::::Po:::::::::::::::o k:::::k k:::::ke::::::eeeee:::::ee +# P:::::::::::::PP o:::::ooooo:::::o k:::::k k:::::ke::::::e e:::::e +# P::::PPPPPPPPP o::::o o::::o k::::::k:::::k e:::::::eeeee::::::e +# P::::P o::::o o::::o k:::::::::::k e:::::::::::::::::e +# P::::P o::::o o::::o k:::::::::::k e::::::eeeeeeeeeee +# P::::P o::::o o::::o k::::::k:::::k e:::::::e +# PP::::::PP o:::::ooooo:::::ok::::::k k:::::ke::::::::e +# P::::::::P o:::::::::::::::ok::::::k k:::::ke::::::::eeeeeeee +# P::::::::P oo:::::::::::oo k::::::k k:::::kee:::::::::::::e +# PPPPPPPPPP ooooooooooo kkkkkkkk kkkkkkk eeeeeeeeeeeeee +# SSSSSSSSSSSSSSS tttt +# SS:::::::::::::::S ttt:::t +# S:::::SSSSSS::::::S t:::::t +# S:::::S SSSSSSS t:::::t +# S:::::S ttttttt:::::ttttttt aaaaaaaaaaaaa rrrrr rrrrrrrrr +# S:::::S t:::::::::::::::::t a::::::::::::a r::::rrr:::::::::r +# S::::SSSS t:::::::::::::::::t aaaaaaaaa:::::ar:::::::::::::::::r +# SS::::::SSSSStttttt:::::::tttttt a::::arr::::::rrrrr::::::r +# SSS::::::::SS t:::::t aaaaaaa:::::a r:::::r r:::::r +# SSSSSS::::S t:::::t aa::::::::::::a r:::::r rrrrrrr +# S:::::S t:::::t a::::aaaa::::::a r:::::r +# S:::::S t:::::t tttttta::::a a:::::a r:::::r +# SSSSSSS S:::::S t::::::tttt:::::ta::::a a:::::a r:::::r +# S::::::SSSSSS:::::S tt::::::::::::::ta:::::aaaa::::::a r:::::r +# S:::::::::::::::SS tt:::::::::::tt a::::::::::aa:::ar:::::r +# SSSSSSSSSSSSSSS ttttttttttt aaaaaaaaaa aaaarrrrrrr +# FFFFFFFFFFFFFFFFFFFFFF +# F::::::::::::::::::::F +# F::::::::::::::::::::F +# FF::::::FFFFFFFFF::::F +# F:::::F FFFFFFaaaaaaaaaaaaa nnnn nnnnnnnn +# F:::::F a::::::::::::a n:::nn::::::::nn +# F::::::FFFFFFFFFF aaaaaaaaa:::::an::::::::::::::nn +# F:::::::::::::::F a::::ann:::::::::::::::n +# F:::::::::::::::F aaaaaaa:::::a n:::::nnnn:::::n +# F::::::FFFFFFFFFF aa::::::::::::a n::::n n::::n +# F:::::F a::::aaaa::::::a n::::n n::::n +# F:::::F a::::a a:::::a n::::n n::::n +# FF:::::::FF a::::a a:::::a n::::n n::::n +# F::::::::FF a:::::aaaa::::::a n::::n n::::n +# F::::::::FF a::::::::::aa:::a n::::n n::::n +# FFFFFFFFFFF aaaaaaaaaa aaaa nnnnnn nnnnnn +# +# +# +# +# +# +# import logging from datetime import datetime