def main(): logging.info("Starting main") with open(os.path.join('..', 'cfg-tlgrm.json'), 'r', encoding='utf-8') as config_file: config_data = json.load(config_file) TOKEN = config_data['TOKEN'] SUBREDDIT = config_data['SUBREDDIT'] CHANNEL = config_data['CHANNEL'] DBFILE = config_data['DBFILE'] logging.info("Setting up Reddit connection") reddit = praw.Reddit(client_id=config.CLIENT_ID, client_secret=config.CLIENT_SECRET, user_agent=config.USER_AGENT) reddit.read_only = True # We will test our reddit connection here if not utils.test_reddit_connection(reddit): exit() subreddit = reddit.subreddit(SUBREDDIT) killhandler = KillHandler() logging.info("Setting up database") conn = sqlite3.connect(DBFILE) conn.execute('CREATE TABLE IF NOT EXISTS posts (id)') conn.commit() logging.info("Setting up Telegram connection") tbot = telegram.Bot(token=TOKEN) try: tbot.get_me() except telegram.error.TelegramError as e_teleg: logging.error(e_teleg) logging.critical("Telegram error!") exit() for submission in subreddit.stream.submissions(pause_after=6): if submission: c = conn.cursor() c.execute('SELECT * FROM posts WHERE id=?', (submission.id, )) if not c.fetchone(): link = 'https://redd.it/{id}'.format(id=submission.id) title = html.escape(submission.title or '') message_template = '<a href=\'{}\'>{}</a>'.format(link, title) logging.info('Posting %s', link) tbot.sendMessage(chat_id=CHANNEL, parse_mode=telegram.ParseMode.HTML, text=message_template) conn.execute('INSERT INTO posts (id) values (?)', (submission.id, )) conn.commit() c.close() if killhandler.killed: logging.info("Termination signal received - exiting") break conn.close()
def main(): killhandler = KillHandler() reddit = praw.Reddit(client_id=config.client_id, client_secret=config.client_secret, username=config.username, password=config.password, user_agent=config.user_agent) logging.info("Starting checking submissions...") stopwatch = Stopwatch() while not killhandler.killed: try: for submission in reddit.subreddit('+'.join( config.subreddits)).stream.submissions(skip_existing=True): duration = stopwatch.measure() logging.info(f"New submission: {submission}") logging.info(f" -- retrieved in {duration:5.2f}s") # We don't need to post a sticky on stickied posts if submission.stickied: logging.info(f" -- skipping (stickied)") continue # Post a comment to let people know where to invest bot_reply = submission.reply_wrap(message.invest_place_here) # Sticky the comment if config.is_moderator: bot_reply.mod.distinguish(how='yes', sticky=True) # Measure how long processing took duration = stopwatch.measure() logging.info(f" -- processed in {duration:5.2f}s") if killhandler.killed: logging.info("Termination signal received - exiting") break except prawcore.exceptions.OAuthException as e_creds: traceback.print_exc() logging.error(e_creds) logging.critical("Invalid login credentials. Check your .env!") logging.critical( "Fatal error. Cannot continue or fix the problem. Bailing out..." ) exit() except Exception as e: logging.error(e) traceback.print_exc() time.sleep(10)
def main(): logging.info("Starting leaderboard...") logging.info( "Sleeping for 8 seconds. Waiting for the database to turn on...") time.sleep(8) killhandler = KillHandler() engine = create_engine(config.DB, pool_recycle=60, pool_pre_ping=True) session_maker = sessionmaker(bind=engine) reddit = praw.Reddit(client_id=config.CLIENT_ID, client_secret=config.CLIENT_SECRET, username=config.USERNAME, password=config.PASSWORD, user_agent=config.USER_AGENT) # We will test our reddit connection here if not utils.test_reddit_connection(reddit): exit() while not killhandler.killed: sess = session_maker() top_users = sess.query( Investor.name, func.coalesce(Investor.balance+func.sum(Investment.amount), Investor.balance).label('networth')).\ outerjoin(Investment, and_(Investor.name == Investment.name, Investment.done == 0)).\ group_by(Investor.name).\ order_by(desc('networth')).\ limit(10).\ all() top_firms = sess.query(Firm).\ filter(Firm.size > 0).\ order_by(Firm.balance.desc()).\ limit(10).\ all() top_users_text = "Rank|User|Net Worth\n" top_users_text += ":-:|:-:|:-:\n" for i, user in enumerate(top_users): top_users_text += f"{i + 1}|/u/{user.name}|{formatNumber(user.networth)} MC\n" top_firms_text = "Rank|Firm|Total Assets|Level|Tax Rate\n" top_firms_text += ":-:|:-:|:-:|:-:|:-:\n" for i, firm in enumerate(top_firms): is_private = '(**P**) ' if firm.private else '' top_firms_text += f"{i + 1}|{is_private}{firm.name}|{formatNumber(firm.balance)} MC|{firm.rank + 1}|{firm.tax}%\n" sidebar_text = sidebar_text_org.\ replace("%TOP_USERS%", top_users_text).\ replace("%TOP_FIRMS%", top_firms_text) logging.info(" -- Updating sidebar text to:") logging.info(sidebar_text) for subreddit in config.SUBREDDITS: reddit.subreddit(subreddit).mod.update(description=sidebar_text) sess.commit() # Report the Reddit API call stats rem = int(reddit.auth.limits['remaining']) res = int(reddit.auth.limits['reset_timestamp'] - time.time()) logging.info(" -- API calls remaining: %s, resetting in %.2fs", rem, res) sess.close() time.sleep(config.LEADERBOARD_INTERVAL)
def main(): killhandler = KillHandler() engine = create_engine(config.db, pool_recycle=60) sm = scoped_session(sessionmaker(bind=engine)) reddit = praw.Reddit(client_id=config.client_id, client_secret=config.client_secret, username=config.username, password=config.password, user_agent=config.user_agent) logging.info("Starting checking submissions...") stopwatch = Stopwatch() while not killhandler.killed: try: sess = sm() submission_time = int(time.time()) for submission in reddit.subreddit('+'.join(config.subreddits)).stream.submissions(skip_existing=True): duration = stopwatch.measure() logging.info(f"New submission: {submission}") logging.info(f" -- retrieved in {duration:5.2f}s") # We don't need to post a sticky on stickied posts if submission.stickied: logging.info(f" -- skipping (stickied)") continue # We are looking if the post is created in the past # so we won't double charge it if (submission.created_utc < submission_time): logging.info(f" -- skipping (timeout)") continue submission_time = int(submission.created_utc) logging.info(f" -- Submission timestamp: {time.asctime(time.gmtime(submission_time))}") bot_reply = 0 delete_post = False # This is a bit of a controversial update, so im gonna make it # agile to switch between different modes if config.submission_fee: # If a poster doesn't have an account, delete the post # if he has, take 1000 MemeCoins and invest them investor = sess.query(Investor).\ filter(Investor.name == submission.author.name).\ first() if not investor: bot_reply = submission.reply_wrap(message.no_account_post_org) delete_post = True logging.info(f" -- Not a registered investor!") elif (investor.balance < 1000): bot_reply = submission.reply_wrap(message.modify_pay_to_post(investor.balance)) delete_post = True logging.info(f" -- Not enough funds!") else: # We will make it 6% required_fee = int(investor.balance * 0.06) if (required_fee < 250): required_fee = 250 new_balance = investor.balance - required_fee investor.balance = new_balance bot_reply = submission.reply_wrap(message.modify_invest_place_here(required_fee)) sess.commit() else: # Post a comment to let people know where to invest bot_reply = submission.reply_wrap(message.invest_place_here_no_fee) # Sticky the comment if config.is_moderator: bot_reply.mod.distinguish(how='yes', sticky=True) if (delete_post): logging.info(f" -- Deleting the post...") #Should we hide or just delete the post? submission.mod.remove() # Measure how long processing took duration = stopwatch.measure() logging.info(f" -- processed in {duration:5.2f}s") if killhandler.killed: logging.info("Termination signal received - exiting") break except prawcore.exceptions.OAuthException as e_creds: traceback.print_exc() logging.error(e_creds) logging.critical("Invalid login credentials. Check your .env!") logging.critical("Fatal error. Cannot continue or fix the problem. Bailing out...") exit() except Exception as e: logging.error(e) traceback.print_exc() time.sleep(10)
def main(): """ This is the main function that listens to new submissions and then posts the ATTENTION sticky comment. """ logging.info("Starting submitter...") logging.info( "Sleeping for 8 seconds. Waiting for the database to turn on...") time.sleep(8) killhandler = KillHandler() engine = create_engine(config.DB, pool_recycle=60, pool_pre_ping=True) sess_maker = scoped_session(sessionmaker(bind=engine)) reddit = praw.Reddit(client_id=config.CLIENT_ID, client_secret=config.CLIENT_SECRET, username=config.USERNAME, password=config.PASSWORD, user_agent=config.USER_AGENT) # We will test our reddit connection here if not utils.test_reddit_connection(reddit): exit() logging.info("Starting checking submissions...") stopwatch = Stopwatch() sess = sess_maker() submission_time = int(time.time()) minimum_fee = config.SUBMISSION_MIN_FEE for submission in reddit.subreddit('+'.join(config.SUBREDDITS)).\ stream.submissions(skip_existing=True): duration = stopwatch.measure() logging.info("New submission: %s", submission) logging.info(" -- retrieved in %ss", duration) # We don't need to post a sticky on stickied posts if submission.stickied: logging.info(" -- skipping (stickied)") continue # We are looking if the post is created in the past # so we won't double charge it if submission.created_utc < submission_time: logging.info(" -- skipping (timeout)") continue submission_time = int(submission.created_utc) logging.info(" -- Submission timestamp: %s", \ time.asctime(time.gmtime(submission_time))) bot_reply = 0 delete_post = False # This is a bit of a controversial update, so im gonna make it # agile to switch between different modes if not config.SUBMISSION_FEE: # Post a comment to let people know where to invest bot_reply = submission.reply_wrap( message.invest_no_fee(f"u/{submission.author.name}")) else: # If a poster doesn't have an account, delete the post # if he has, take 1000 MemeCoins and invest them investor = sess.query(Investor).\ filter(Investor.name == submission.author.name).\ first() if not investor: bot_reply = submission.reply_wrap(message.NO_ACCOUNT_POST_ORG) delete_post = True logging.info(" -- Not a registered investor!") elif investor.balance < minimum_fee: bot_reply = submission.reply_wrap( message.modify_pay_to_post(investor.balance)) delete_post = True logging.info(" -- Not enough funds!") else: # We will make it 6% required_fee = int(investor.balance * (config.SUBMISSION_FEE_PERCENT / 100)) if required_fee < minimum_fee: required_fee = minimum_fee new_balance = investor.balance - required_fee investor.balance = new_balance bot_reply = submission.\ reply_wrap(message.modify_invest_place_here(required_fee, f"u/{submission.author.name}")) sess.commit() # Sticky the comment if config.IS_MODERATOR: bot_reply.mod.distinguish(how='yes', sticky=True) bot_reply.mod.approve() if delete_post: logging.info(" -- Deleting the post...") #Should we hide or just delete the post? submission.mod.remove() # Measure how long processing took duration = stopwatch.measure() logging.info(" -- processed in %.2fs", duration) if killhandler.killed: logging.info("Termination signal received - exiting") break
def main(): logging.info("Starting payroll...") logging.info( "Sleeping for 8 seconds. Waiting for the database to turn on...") time.sleep(8) killhandler = KillHandler() engine = create_engine(config.DB, pool_recycle=60, pool_pre_ping=True) session_maker = sessionmaker(bind=engine) while not killhandler.killed: # only process payouts 5pm - 6pm on Fridays EST (10pm - 11pm UTC) now_dt = datetime.datetime.now() if now_dt.hour != 22 or now_dt.weekday() != 4: time.sleep(10 * 60) continue sess = session_maker() now = time.time() # get firms which were not paid out to or created recently (last 3 days) firms = sess.query(Firm).\ filter(now - Firm.last_payout >= (3 * 24 * 60 * 60)).\ all() for firm in firms: # 10% of firm coins are burned as a tax firm.balance -= int(0.1 * firm.balance) payout_amount = int(0.4 * firm.balance) if payout_amount == 0: # handle broke firms firm.last_payout = now continue exec_amount = 0 exec_total = 0 if firm.execs > 0: exec_total = payout_amount * 0.4 exec_amount = int(exec_total / firm.execs) trader_total = payout_amount - exec_total trader_amount = int(trader_total / (firm.size - firm.execs)) logging.info(" -- firm '%s': paying out %s each to %s traders, and %s each to %s execs",\ firm.name, trader_amount, firm.size - firm.execs, exec_amount, firm.execs) employees = sess.query(Investor).\ filter(Investor.firm == firm.id).\ all() for employee in employees: if employee.firm_role == "": employee.balance += trader_amount elif employee.firm_role == "exec": employee.balance += exec_amount firm.balance -= payout_amount firm.last_payout = now sess.commit() sess.close() time.sleep(10 * 60)
def main(): logging.info("Starting payroll...") logging.info("Sleeping for 8 seconds. Waiting for the database to turn on...") time.sleep(8) killhandler = KillHandler() engine = create_engine(config.DB, pool_recycle=60, pool_pre_ping=True) session_maker = sessionmaker(bind=engine) while not killhandler.killed: # only process payouts 5pm - 6pm on Fridays EST (10pm - 11pm UTC) now_dt = datetime.datetime.now() if now_dt.hour != 22 or now_dt.weekday() != 4: time.sleep(10 * 60) continue sess = session_maker() now = time.time() # get firms which were not paid out to or created recently (last 3 days) firms = sess.query(Firm).\ filter(now - Firm.last_payout >= (3 * 24 * 60 * 60)).\ all() for firm in firms: # 10% of firm coins are burned as a tax firm.balance -= int(0.1 * firm.balance) # 50% of remaining firm coins are paid out payout_amount = int(0.5 * firm.balance) if payout_amount == 0: # handle broke firms firm.last_payout = now continue # 30% paid to board members (CEO, COO, CFO) (30% of total payroll) board_total = payout_amount * 0.3 board_members = 1 + firm.coo + firm.cfo board_amount = int(board_total / board_members) remaining_amount = payout_amount - board_total # 40% of remaining paid to executives (28% of total payroll) exec_amount = 0 exec_total = 0 if firm.execs > 0: exec_total = remaining_amount * 0.4 exec_amount = int(exec_total / firm.execs) remaining_amount -= exec_total # 50% of remaining paid to associates (21% of total payroll) assoc_amount = 0 assoc_total = 0 if firm.assocs > 0: assoc_total = remaining_amount * 0.5 assoc_amount = int(assoc_total / firm.assocs) remaining_amount -= assoc_total # 100% of remaining paid to associates (21% of total payroll) trader_total = remaining_amount trader_amount = int(trader_total / max(firm.size - firm.execs, 1)) logging.info(" -- firm '%s': paying out %s each to %s trader(s), %s each to %s associate(s), %s each to %s executive(s), and %s each to %s board member(s)",\ firm.name, trader_amount, firm.size - firm.execs, assoc_amount, firm.assocs, exec_amount, firm.execs, board_amount, board_members) employees = sess.query(Investor).\ filter(Investor.firm == firm.id).\ all() for employee in employees: if employee.firm_role == "": employee.balance += trader_amount elif employee.firm_role == "assoc": employee.balance += assoc_amount elif employee.firm_role == "exec": employee.balance += exec_amount elif employee.firm_role == "cfo": employee.balance += board_amount elif employee.firm_role == "coo": employee.balance += board_amount elif employee.firm_role == "ceo": employee.balance += board_amount firm.balance -= payout_amount firm.last_payout = now sess.commit() sess.close() time.sleep(10 * 60)
def main(): logging.info("Starting main") if config.post_to_reddit: logging.info("Warning: Bot will actually post to Reddit!") logging.info("Setting up database") killhandler = KillHandler() engine = create_engine(config.db, pool_recycle=60) sm = scoped_session(sessionmaker(bind=engine)) worker = CommentWorker(sm) while True: try: Base.metadata.create_all(engine) break except sqlalchemy.exc.OperationalError: logging.info("Database not available yet; retrying in 5s") time.sleep(5) logging.info("Setting up Reddit connection") reddit = praw.Reddit(client_id=config.client_id, client_secret=config.client_secret, username=config.username, password=config.password, user_agent=config.user_agent) stopwatch = Stopwatch() logging.info("Listening for inbox replies...") while not killhandler.killed: try: # Iterate over the latest comment replies in inbox reply_function = reddit.inbox.comment_replies if (config.maintenance): logging.info( "ENTERING MAINTENANCE MODE. NO OPERATIONS WILL BE PROCESSED." ) for comment in praw.models.util.stream_generator( reply_function): logging.info(f"New comment {comment}:") if comment.new: comment.reply_wrap(message.maintenance_org) comment.mark_read() for comment in praw.models.util.stream_generator(reply_function): # Measure how long since we finished the last loop iteration duration = stopwatch.measure() logging.info(f"New comment {comment}:") logging.info(f" -- retrieved in {duration:5.2f}s") if comment.new: # Process the comment worker(comment) # Mark the comment as processed comment.mark_read() else: logging.info(" -- skipping (already processed)") # Measure how long processing took duration = stopwatch.measure() logging.info(f" -- processed in {duration:5.2f}s") # Report the Reddit API call stats rem = int(reddit.auth.limits['remaining']) res = int(reddit.auth.limits['reset_timestamp'] - time.time()) logging.info( f" -- API calls remaining: {rem:3d}, resetting in {res:3d}s" ) # Check for termination requests if killhandler.killed: logging.info("Termination signal received - exiting") break stopwatch.reset() except prawcore.exceptions.OAuthException as e_creds: traceback.print_exc() logging.error(e_creds) logging.critical("Invalid login credentials. Check your .env!") logging.critical( "Fatal error. Cannot continue or fix the problem. Bailing out..." ) exit() except Exception as e: logging.error(e) traceback.print_exc() time.sleep(10)
def main(): logging.info("Starting calculator...") killhandler = KillHandler() engine = create_engine() session_maker = sessionmaker(bind=engine) reddit = praw.Reddit( client_id=config.CLIENT_ID, client_secret=config.CLIENT_SECRET, username=config.USERNAME, password=config.PASSWORD, user_agent=config.USER_AGENT, ) # We will test our reddit connection here if not utils.test_reddit_connection(reddit): return () praw.models.Comment.edit_wrap = edit_wrap stopwatch = Stopwatch() logging.info("Retrieving top ...") # query sess = session_maker() try: top_networth = (sess.query( Investor.name, func.coalesce(Investor.balance + func.sum(Investment.amount), Investor.balance).label("networth"), ).outerjoin( Investment, and_(Investor.name == Investment.name, Investment.done == 0)).group_by(Investor.name).order_by( desc("networth")).limit(1).one())[1] except NoResultFound: top_networth = 0 top_networth = max(top_networth, config.STARTING_BALANCE * 10) # al last starting * 10 sess.close() logging.info("Top networth: %d", top_networth) logging.info("Monitoring active investments...") while not killhandler.killed: sess = session_maker() then = int(time.time()) - config.INVESTMENT_DURATION investment = (sess.query(Investment).filter( Investment.done == 0).filter(Investment.time < then).order_by( Investment.time.asc()).first()) if not investment: # Nothing matured yet; wait a bit before trying again time.sleep(50) continue duration = stopwatch.measure() investor = sess.query(Investor).filter( Investor.name == investment.name).one() net_worth = investor.networth(sess) logging.info("New mature investment: %s", investment.comment) logging.info(" -- by %s", investor.name) # Retrieve the post the user invested in (lazily, no API call) post = reddit.submission(investment.post) # Retrieve the post's current upvote count (triggers an API call) upvotes_now = post.ups investment.final_upvotes = upvotes_now investment.op = (post.author and investor.name == post.author.name) investment.net_worth = net_worth investment.top_networth = top_networth # Updating the investor's balance factor = formula.calculate(upvotes_now, investment.upvotes, net_worth, top_networth) if factor > 1 and post.author and investor.name == post.author.name: # bonus per OP factor *= formula.OP_BONUS amount = investment.amount balance = investor.balance new_balance = int(balance + (amount * factor)) change = new_balance - balance profit = change - amount # Updating the investor's variables investor.completed += 1 # Retrieve the bot's original response (lazily, no API call) if investment.response != "0": response = reddit.comment(id=investment.response) else: response = EmptyResponse() if new_balance < BALANCE_CAP: # If investor is in a firm and he profits, # 15% goes to the firm investor.balance = new_balance # Edit the bot's response (triggers an API call) if profit > 0: logging.info(" -- profited %s", profit) elif profit == 0: logging.info(" -- broke even") else: logging.info(" -- lost %s", profit) edited_response = message.modify_invest_return( investment.amount, investment.upvotes, upvotes_now, change, profit, investor.balance, ) response.edit_wrap(edited_response) else: # This investment pushed the investor's balance over the cap investor.balance = BALANCE_CAP # Edit the bot's response (triggers an API call) logging.info(" -- profited %s but got capped", profit) response.edit_wrap( message.modify_invest_capped( investment.amount, investment.upvotes, upvotes_now, change, profit, investor.balance, )) investment.success = profit > 0 investment.profit = profit investment.done = True sess.commit() if top_networth < investor.balance: top_networth = investor.balance logging.info("New Top networth: %d", top_networth) # Measure how long processing took duration = stopwatch.measure() logging.info(" -- processed in %.2fs", duration) # Report the Reddit API call stats rem = int(reddit.auth.limits["remaining"]) res = int(reddit.auth.limits["reset_timestamp"] - time.time()) logging.info(" -- API calls remaining: %s, resetting in %.2fs", rem, res) sess.close()
def main(): logging.info("Starting calculator...") logging.info( "Sleeping for 8 seconds. Waiting for the database to turn on...") time.sleep(8) killhandler = KillHandler() engine = create_engine(config.DB, pool_recycle=60, pool_pre_ping=True) session_maker = sessionmaker(bind=engine) reddit = praw.Reddit(client_id=config.CLIENT_ID, client_secret=config.CLIENT_SECRET, username=config.USERNAME, password=config.PASSWORD, user_agent=config.USER_AGENT) # We will test our reddit connection here if not utils.test_reddit_connection(reddit): exit() praw.models.Comment.edit_wrap = edit_wrap stopwatch = Stopwatch() logging.info("Monitoring active investments...") while not killhandler.killed: sess = session_maker() then = int(time.time()) - config.INVESTMENT_DURATION investment = sess.query(Investment).\ filter(Investment.done == 0).\ filter(Investment.time < then).\ order_by(Investment.time.asc()).\ first() if not investment: # Nothing matured yet; wait a bit before trying again time.sleep(5) continue duration = stopwatch.measure() investor = sess.query(Investor).filter( Investor.name == investment.name).one() net_worth = sess.\ query(func.sum(Investment.amount)).\ filter(and_(Investment.name == investor.name, Investment.done == 0)).\ scalar()\ + investor.balance logging.info("New mature investment: %s", investment.comment) logging.info(" -- by %s", investor.name) # Retrieve the post the user invested in (lazily, no API call) post = reddit.submission(investment.post) # Retrieve the post's current upvote count (triggers an API call) upvotes_now = post.ups investment.final_upvotes = upvotes_now # Updating the investor's balance factor = formula.calculate(upvotes_now, investment.upvotes, net_worth) amount = investment.amount balance = investor.balance new_balance = int(balance + (amount * factor)) change = new_balance - balance profit = change - amount percent_str = f"{int((profit/amount)*100)}%" # Updating the investor's variables investor.completed += 1 # Retrieve the bot's original response (lazily, no API call) if investment.response != "0": response = reddit.comment(id=investment.response) else: response = EmptyResponse() firm_profit = 0 if new_balance < BALANCE_CAP: # If investor is in a firm and he profits, # 15% goes to the firm firm_name = '' if investor.firm != 0 and profit >= 0: firm = sess.query(Firm).\ filter(Firm.id == investor.firm).\ first() firm_name = firm.name user_profit = int(profit * ((100 - firm.tax) / 100)) investor.balance += user_profit + amount firm_profit = int(profit * (firm.tax / 100)) firm.balance += firm_profit else: investor.balance = new_balance # Edit the bot's response (triggers an API call) if profit > 0: logging.info(" -- profited %s", profit) elif profit == 0: logging.info(" -- broke even") else: logging.info(" -- lost %s", profit) edited_response = message.modify_invest_return( investment.amount, investment.upvotes, upvotes_now, change, profit, percent_str, investor.balance) if investor.firm != 0: edited_response += message.modify_firm_tax( firm_profit, firm_name) response.edit_wrap(edited_response) else: # This investment pushed the investor's balance over the cap investor.balance = BALANCE_CAP # Edit the bot's response (triggers an API call) logging.info(" -- profited %s but got capped", profit) response.edit_wrap( message.modify_invest_capped(investment.amount, investment.upvotes, upvotes_now, change, profit, percent_str, investor.balance)) investment.success = (profit > 0) investment.profit = profit investment.done = True sess.commit() # Measure how long processing took duration = stopwatch.measure() logging.info(" -- processed in %.2fs", duration) # Report the Reddit API call stats rem = int(reddit.auth.limits['remaining']) res = int(reddit.auth.limits['reset_timestamp'] - time.time()) logging.info(" -- API calls remaining: %s, resetting in %.2fs", rem, res) sess.close()
def main(): logging.info("Starting calculator") killhandler = KillHandler() engine = create_engine(config.db, pool_recycle=60) sm = sessionmaker(bind=engine) reddit = praw.Reddit(client_id=config.client_id, client_secret=config.client_secret, username=config.username, password=config.password, user_agent=config.user_agent) praw.models.Comment.edit_wrap = edit_wrap stopwatch = Stopwatch() logging.info("Monitoring active investments...") while not killhandler.killed: try: sess = sm() then = int(time.time()) - config.investment_duration investment = sess.query(Investment).\ filter(Investment.done == 0).\ filter(Investment.time < then).\ order_by(Investment.time.asc()).\ first() if not investment: # Nothing matured yet; wait a bit before trying again time.sleep(5) continue duration = stopwatch.measure() investor = sess.query(Investor).filter(Investor.name == investment.name).one() logging.info(f"New mature investment: {investment.comment}") logging.info(f" -- by {investor.name}") # Retrieve the post the user invested in (lazily, no API call) post = reddit.submission(investment.post) # Retrieve the post's current upvote count (triggers an API call) upvotes_now = post.ups investment.final_upvotes = upvotes_now # Updating the investor's balance factor = formula.calculate(upvotes_now, investment.upvotes) amount = investment.amount balance = investor.balance new_balance = int(balance + (amount * factor)) change = new_balance - balance profit = change - amount percent_str = f"{int((profit/amount)*100)}%" # Updating the investor's variables investor.completed += 1 # Retrieve the bot's original response (lazily, no API call) if investment.response != "0": response = reddit.comment(id=investment.response) else: response = EmptyResponse() if new_balance < BalanceCap: investor.balance = new_balance # Edit the bot's response (triggers an API call) if profit > 0: logging.info(f" -- profited {profit}") elif profit == 0: logging.info(f" -- broke even") else: logging.info(f" -- lost {profit}") response.edit_wrap(message.modify_invest_return(investment.amount, investment.upvotes, upvotes_now, change, profit, percent_str, investor.balance)) else: # This investment pushed the investor's balance over the cap investor.balance = BalanceCap # Edit the bot's response (triggers an API call) logging.info(f" -- profited {profit} but got capped") response.edit_wrap(message.modify_invest_capped(investment.amount, investment.upvotes, upvotes_now, change, profit, percent_str, investor.balance)) investment.success = (profit > 0) investment.profit = profit investment.done = True sess.commit() # Measure how long processing took duration = stopwatch.measure() logging.info(f" -- processed in {duration:5.2f}s") # Report the Reddit API call stats rem = int(reddit.auth.limits['remaining']) res = int(reddit.auth.limits['reset_timestamp'] - time.time()) logging.info(f" -- API calls remaining: {rem:3d}, resetting in {res:3d}s") except prawcore.exceptions.OAuthException as e_creds: traceback.print_exc() logging.error(e_creds) logging.critical("Invalid login credentials. Check your .env!") logging.critical("Fatal error. Cannot continue or fix the problem. Bailing out...") exit() except Exception as e: logging.error(e) traceback.print_exc() time.sleep(10) finally: sess.close()
def main() -> None: """ This is the main function that listens to new submissions and then posts the ATTENTION sticky comment. """ logging.info("Starting submitter...") killhandler = KillHandler() engine = create_engine() sess_maker = scoped_session(sessionmaker(bind=engine)) reddit = praw.Reddit( client_id=config.CLIENT_ID, client_secret=config.CLIENT_SECRET, username=config.USERNAME, password=config.PASSWORD, user_agent=config.USER_AGENT, ) logging.info("Setting up database") conn = sqlite3.connect(config.POST_DBFILE) conn.execute("CREATE TABLE IF NOT EXISTS posts (id)") conn.commit() logging.info("Setting up Telegram connection") tbot = telegram.Bot(token=config.TG_TOKEN) try: tbot.get_me() except telegram.error.TelegramError as e_teleg: logging.error(e_teleg) logging.critical("Telegram error!") return # We will test our reddit connection here if not test_reddit_connection(reddit): return logging.info("Starting checking submissions...") stopwatch = Stopwatch() sess = sess_maker() subreddits = reddit.subreddit("+".join(config.SUBREDDITS)) for submission in subreddits.stream.submissions(pause_after=6): if killhandler.killed: logging.info("Termination signal received - exiting") break if not submission: # because of pause_after # to handle ctr+c above continue duration = stopwatch.measure() logging.info("New submission: %s", submission) logging.info(" -- retrieved in %ss", duration) c = conn.cursor() c.execute("SELECT * FROM posts WHERE id=?", (submission.id,)) if c.fetchone(): logging.info("Already processed") continue post_telegram(conn, submission, tbot) bot_reply = post_reply(submission) # Measure how long processing took duration = stopwatch.measure() logging.info(" -- processed in %.2fs", duration) # Create Buyable if bot_reply: sess.add( Buyable(post=submission.id, name=submission.author.name, response=bot_reply.id) ) sess.commit()
def main(): """ This is where the magic happens. This function listens to all new messages in the inbox and passes them to worker object that decides on what to do with them. """ logging.info("Starting main") if config.POST_TO_REDDIT: logging.info("Warning: Bot will actually post to Reddit!") logging.info("Setting up database") killhandler = KillHandler() engine = create_engine(config.DB, pool_recycle=60, pool_pre_ping=True) session_maker = scoped_session(sessionmaker(bind=engine)) worker = CommentWorker(session_maker) while True: try: Base.metadata.create_all(engine) break except sqlalchemy.exc.OperationalError: logging.info("Database not available yet; retrying in 5s") time.sleep(5) logging.info("Setting up Reddit connection") reddit = praw.Reddit(client_id=config.CLIENT_ID, client_secret=config.CLIENT_SECRET, username=config.USERNAME, password=config.PASSWORD, user_agent=config.USER_AGENT) # We will test our reddit connection here if not utils.test_reddit_connection(reddit): exit() stopwatch = Stopwatch() logging.info("Listening for inbox replies...") while not killhandler.killed: # Iterate over the latest comment replies in inbox reply_function = reddit.inbox.comment_replies if config.MAINTENANCE: logging.info( "ENTERING MAINTENANCE MODE. NO OPERATIONS WILL BE PROCESSED.") for comment in praw.models.util.stream_generator(reply_function): logging.info("New comment %s:", comment) if comment.new: comment.reply_wrap(message.MAINTENANCE_ORG) comment.mark_read() for comment in praw.models.util.stream_generator(reply_function): # Measure how long since we finished the last loop iteration duration = stopwatch.measure() logging.info("New comment %s (%s):", comment, type(comment)) logging.info(" -- retrieved in %.2fs", duration) if comment.new: if comment.subreddit.display_name.lower() in config.SUBREDDITS: # Process the comment only in allowed subreddits worker(comment) else: logging.info(" -- skipping (wrong subreddit)") # Mark the comment as processed comment.mark_read() else: logging.info(" -- skipping (already processed)") # Measure how long processing took duration = stopwatch.measure() logging.info(" -- processed in %.2fs", duration) # Report the Reddit API call stats rem = int(reddit.auth.limits['remaining']) res = int(reddit.auth.limits['reset_timestamp'] - time.time()) logging.info(" -- API calls remaining: %.2f, resetting in %.2fs", rem, res) # Check for termination requests if killhandler.killed: logging.info("Termination signal received - exiting") break stopwatch.reset()
def main(): logging.info("Starting calculator") killhandler = KillHandler() engine = create_engine(config.db, pool_recycle=60) sm = sessionmaker(bind=engine) reddit = praw.Reddit(client_id=config.client_id, client_secret=config.client_secret, username=config.username, password=config.password, user_agent=config.user_agent) praw.models.Comment.edit_wrap = edit_wrap stopwatch = Stopwatch() logging.info("Monitoring active investments...") while not killhandler.killed: try: sess = sm() then = int(time.time()) - config.investment_duration investment = sess.query(Investment).\ filter(Investment.done == 0).\ filter(Investment.time < then).\ order_by(Investment.time.asc()).\ first() if not investment: # Nothing matured yet; wait a bit before trying again time.sleep(5) continue duration = stopwatch.measure() investor = sess.query(Investor).filter(Investor.name == investment.name).one() logging.info(f"New mature investment: {investment.comment}") logging.info(f" -- by {investor.name}") if investment.response != "0": response = reddit.comment(id=investment.response) else: response = EmptyResponse() post = reddit.submission(investment.post) upvotes_now = post.ups # <--- triggers a Reddit API call # Updating the investor's balance factor = formula.calculate(upvotes_now, investment.upvotes) amount = investment.amount balance = investor.balance new_balance = int(balance + (amount * factor)) change = new_balance - balance profit = change - amount profit_str = f"{int((profit/amount)*100)}%" # Updating the investor's variables investor.completed += 1 investor.balance = new_balance # Editing the comment as a confirmation text = response.body # <--- triggers a Reddit API call if profit > 0: logging.info(f" -- profited {profit}") response.edit_wrap(message.modify_invest_return(text, upvotes_now, change, profit_str, new_balance)) elif profit == 0: logging.info(f" -- broke even") response.edit_wrap(message.modify_invest_break_even(text, upvotes_now, change, profit_str, new_balance)) else: lost_memes = int( amount - change ) logging.info(f" -- lost {profit}") response.edit_wrap(message.modify_invest_lose(text, upvotes_now, lost_memes, profit_str, new_balance)) investment.success = (profit > 0) investment.profit = profit investment.done = True sess.commit() # Measure how long processing took duration = stopwatch.measure() logging.info(f" -- processed in {duration:5.2f}s") # Report the Reddit API call stats rem = int(reddit.auth.limits['remaining']) res = int(reddit.auth.limits['reset_timestamp'] - time.time()) logging.info(f" -- API calls remaining: {rem:3d}, resetting in {res:3d}s") except Exception as e: logging.error(e) traceback.print_exc() time.sleep(10) finally: sess.close()