Beispiel #1
0
 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
Beispiel #2
0
    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)
Beispiel #3
0
 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
Beispiel #4
0
 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
Beispiel #5
0
 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
Beispiel #6
0
 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
Beispiel #7
0
 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
Beispiel #8
0
    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)