async def start_giveaway_bot(server_id: int, entry_fee: float, started_in_channel: int, conn=None) -> 'Giveaway': # Double check no active giveaways active = await Giveaway.get_active_giveaway(server_id) if active is not None: raise Exception("There's already an active giveaway") giveaway = Giveaway(started_by_bot=True, base_amount=str(Env.amount_to_raw(0)), entry_fee=str(Env.amount_to_raw(entry_fee)), server_id=server_id, started_in_channel=started_in_channel) await giveaway.save(using_db=conn) return giveaway
async def register_cmd(self, ctx: Context): if ctx.error: return msg = ctx.message try: amount = RegexUtil.find_float(msg.content) except AmountMissingException: amount = 0.0 # Get/create user try: user = await User.create_or_fetch_user(msg.author) user_address = await user.get_address() except Exception: self.logger.exception('Exception creating user') await Messages.send_error_dm(msg.author, "I failed at retrieving your address, try again later and contact my master if the issue persists.") return # Build URI uri_scheme = "ban:" if Env.banano() else "watermelonano:" if amount == 0: uri = user_address else: uri = "{0}{1}?amount={2}".format(uri_scheme, user_address, Env.amount_to_raw(amount)) # Build and send response embed = discord.Embed(colour=0xFBDD11 if Env.banano() else discord.Colour.green()) embed.set_author(name=user_address, icon_url="https://github.com/bbedward/Graham_Nano_Tip_Bot/raw/master/assets/banano_logo.png" if Env.banano() else "https://i.imgur.com/7QFgoqT.png") embed.set_image(url=f"https://chart.googleapis.com/chart?cht=qr&chl={uri}&chs=180x180&choe=UTF-8&chld=L|2") await msg.author.send(embed=embed) await msg.author.send(user_address)
async def start_giveaway_user(server_id: int, started_by: usr.User, amount: float, entry_fee: float, duration: int, started_in_channel: int, conn=None) -> 'Giveaway': # Double check no active giveaways active = await Giveaway.get_active_giveaway(server_id) if active is not None: raise Exception("There's already an active giveaway") giveaway = Giveaway(started_by=started_by, base_amount=str(Env.amount_to_raw(amount)), entry_fee=str(Env.amount_to_raw(entry_fee)), end_at=datetime.datetime.utcnow() + datetime.timedelta(minutes=duration), server_id=server_id, started_in_channel=started_in_channel) await giveaway.save(using_db=conn) return giveaway
async def create_transaction_giveaway(sending_user: usr.User, amount: float, giveaway: gway.Giveaway, conn=None) -> 'Transaction': """Create a transaction in the database, among discord users""" # Create transaction tx = None tx = Transaction(sending_user=sending_user, amount=str(Env.amount_to_raw(amount)), giveaway=giveaway) await tx.save(using_db=conn) return tx
async def create_transaction_external(sending_user: usr.User, amount: float, destination: str) -> 'Transaction': # Create transaction tx = None async with in_transaction() as conn: tx = Transaction(sending_user=sending_user, amount=str(Env.amount_to_raw(amount)), destination=destination, receiving_user=None) await tx.save(using_db=conn) return tx
async def create_transaction_internal_dbuser( sending_user: usr.User, amount: float, receiving_user: usr.User) -> 'Transaction': """Create a transaction in the database, among discord users""" # Create transaction tx = None async with in_transaction() as conn: tx = Transaction(sending_user=sending_user, amount=str(Env.amount_to_raw(amount)), destination=await receiving_user.get_address(), receiving_user=receiving_user) await tx.save(using_db=conn) return tx
async def create_transaction_internal( sending_user: usr.User, amount: float, receiving_user: discord.User) -> 'Transaction': """Create a transaction in the database, among discord users""" # See if receiving user exists in our database receiving_user_db: usr.User = await usr.User.create_or_fetch_user( receiving_user) if receiving_user_db.tip_banned: return None # Create transaction tx = None async with in_transaction() as conn: tx = Transaction(sending_user=sending_user, amount=str(Env.amount_to_raw(amount)), destination=await receiving_user_db.get_address(), receiving_user=receiving_user_db) await tx.save(using_db=conn) return tx
async def tipgiveaway_cmd(self, ctx: Context): if ctx.error: return msg = ctx.message user = ctx.user # Check roles if not await self.role_check(msg): return # Punish them for trying to do this command in a no spam channel if msg.channel.id in config.Config.instance().get_no_spam_channels(): redis_key = f"ticketspam:{msg.author.id}" if not ctx.god: spam = await RedisDB.instance().get(redis_key) if spam is not None: spam = int(spam) else: spam = 0 await Messages.add_x_reaction(msg) await Messages.send_error_dm(msg.author, "You can't view donate to the giveaway in this channel") await RedisDB.instance().set(f"ticketspam:{msg.author.id}", str(spam + 1), expires=3600) await Messages.delete_message_if_ok(msg) return # Get their tip amount try: tip_amount = RegexUtil.find_float(msg.content) if tip_amount < Constants.TIP_MINIMUM: await Messages.send_error_dm(msg.author, f"Minimum tip amount if {Constants.TIP_MINIMUM}") await Messages.delete_message_if_ok(msg) return except AmountMissingException: await Messages.send_usage_dm(msg.author, TIPGIVEAWAY_INFO) await Messages.delete_message_if_ok(msg) return # Get active giveaway gw = await Giveaway.get_active_giveaway(server_id=msg.guild.id) if gw is None: # get bot-pending giveaway or create one gw = await Giveaway.get_pending_bot_giveaway(server_id=msg.guild.id) if gw is None: try: # Initiate the bot giveaway with a lock to avoid race condition # Lock this so concurrent giveaways can't be started/avoid race condition async with RedisLock( await RedisDB.instance().get_redis(), key=f"{Env.currency_symbol().lower()}giveawaylock:{msg.guild.id}", timeout=30, wait_timeout=30 ) as lock: # See if giveaway already in progress should_create = False active_giveaway = await Giveaway.get_active_giveaway(server_id=msg.guild.id) if active_giveaway is None: bot_giveaway = await Giveaway.get_pending_bot_giveaway(server_id=msg.guild.id) if bot_giveaway is None: should_create = True if should_create: # Start giveaway async with in_transaction() as conn: gw = await Giveaway.start_giveaway_bot( server_id=msg.guild.id, entry_fee=config.Config.instance().get_giveaway_auto_fee(), started_in_channel=msg.channel.id, conn=conn ) except LockTimeoutError: gw = await Giveaway.get_pending_bot_giveaway(server_id=msg.guild.id) if gw is None: await Messages.send_error_dm(msg.author, "I was unable to process your donation, try again alter!") await Messages.delete_message_if_ok(msg) return # Check balance available_balance = Env.raw_to_amount(await user.get_available_balance()) if tip_amount > available_balance: if not ctx.god: redis_key = f"ticketspam:{msg.author.id}" spam = await RedisDB.instance().get(redis_key) if spam is not None: spam = int(spam) else: spam = 0 await RedisDB.instance().set(f"ticketspam:{msg.author.id}", str(spam + 1), expires=3600) await Messages.add_x_reaction(msg) await Messages.send_error_dm(msg.author, "Your balance isn't high enough to complete this tip.") await Messages.delete_message_if_ok(msg) return # See if they already contributed user_tx = await Transaction.filter(giveaway__id=gw.id, sending_user__id=user.id).first() already_entered = False async with in_transaction() as conn: if user_tx is not None: if int(user_tx.amount) >= int(gw.entry_fee): already_entered=True user_tx.amount = str(int(user_tx.amount) + Env.amount_to_raw(tip_amount)) await user_tx.save(update_fields=['amount'], using_db=conn) else: user_tx = await Transaction.create_transaction_giveaway( user, tip_amount, gw, conn=conn ) if gw.end_at is None: if not already_entered and int(user_tx.amount) >= int(gw.entry_fee): await Messages.send_success_dm(msg.author, f"With your generous donation of {Env.raw_to_amount(int(user_tx.amount))} {Env.currency_symbol()} I have reserved your spot for giveaway #{gw.id}!") else: await Messages.send_success_dm(msg.author, f"Your generous donation of {Env.raw_to_amount(int(user_tx.amount))} {Env.currency_symbol()} will help support giveaway #{gw.id}!") # See if we should start this giveaway, and start it if so # TODO - We should use the DB SUM() function but,we store it as a VarChar and tortoise-orm currently doesn't support casting giveaway_sum_raw = 0 for tx in await Transaction.filter(giveaway=gw): giveaway_sum_raw += int(tx.amount) giveaway_sum = Env.raw_to_amount(giveaway_sum_raw) if giveaway_sum >= config.Config.instance().get_giveaway_auto_minimum(): # start giveaway # re-fetch latest version gw = await Giveaway.get_pending_bot_giveaway(server_id=msg.guild.id) if gw is not None: gw.end_at = datetime.datetime.utcnow() + datetime.timedelta(minutes=config.Config.instance().get_giveaway_auto_duration()) gw.started_in_channel = msg.channel.id async with in_transaction() as conn: await gw.save(update_fields=['end_at', 'started_in_channel'], using_db=conn) # Announce giveaway embed = self.format_giveaway_announcement(gw, amount=giveaway_sum_raw) await msg.channel.send(embed=embed) for ch in config.Config.instance().get_giveaway_announce_channels(): if ch != msg.channel.id: channel = msg.guild.get_channel(ch) if channel is not None: try: await channel.send(embed=embed) except Exception: pass # Start the timer asyncio.create_task(self.start_giveaway_timer(gw)) else: if not already_entered and int(user_tx.amount) >= int(gw.entry_fee): await Messages.send_success_dm(msg.author, f"With your generous donation of {tip_amount} {Env.currency_symbol()} I have entered you into giveaway #{gw.id}!") else: await Messages.send_success_dm(msg.author, f"Your generous donation of {tip_amount} {Env.currency_symbol()} will help support giveaway #{gw.id}!") if msg.channel.id in config.Config.instance().get_giveaway_no_delete_channels(): await Messages.add_tip_reaction(msg, tip_amount) await Messages.delete_message_if_ok(msg) # Update stats stats: Stats = await user.get_stats(server_id=msg.guild.id) if msg.channel.id not in config.Config.instance().get_no_stats_channels(): await stats.update_tip_stats(tip_amount)