Exemplo n.º 1
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
Exemplo n.º 2
0
    async def tipauthor_cmd(self, ctx: Context):
        if ctx.error:
            await Messages.add_x_reaction(ctx.message)
            return

        msg = ctx.message
        user = ctx.user
        send_amount = ctx.send_amount

        # See how much they need to make this tip.
        available_balance = Env.raw_to_amount(await
                                              user.get_available_balance())
        if send_amount > available_balance:
            await Messages.add_x_reaction(ctx.message)
            await Messages.send_error_dm(
                msg.author,
                f"Your balance isn't high enough to complete this tip. You have **{available_balance} {Env.currency_symbol()}**, but this tip would cost you **{send_amount} {Env.currency_symbol()}**"
            )
            return

        # Make the transactions in the database
        tx_list = []
        task_list = []
        tx = await Transaction.create_transaction_external(
            sending_user=user,
            amount=send_amount,
            destination=Env.donation_address())
        # Add reactions
        await msg.add_reaction('\U00002611')
        await msg.add_reaction('\U0001F618')
        await msg.add_reaction('\u2764')
        await msg.add_reaction('\U0001F499')
        await msg.add_reaction('\U0001F49B')
        # Queue the actual send
        await TransactionQueue.instance().put(tx)
        # 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(send_amount)
Exemplo n.º 3
0
 def __init__(self, bot: Bot, host: str, port: int):
     self.bot = bot
     self.app = web.Application()
     self.app.add_routes([
         web.post('/callback', self.callback),
         web.get('/ufw/{wallet}', self.ufw),
         web.get('/wfu/{user}', self.wfu),
         web.get('/users', self.users)
     ])
     self.logger = logging.getLogger()
     self.host = host
     self.port = port
     self.min_amount = 10 if Env.banano() else 0.1
Exemplo n.º 4
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
Exemplo n.º 5
0
 def get_help_pages(self, cmd_dict: dict, adminhelp: bool = False) -> list:
     """Builds paginated help menu"""
     pages = []
     # Overview
     author = f"Graham v{__version__} ({'BANANO' if Env.banano() else 'Nano'}) edition"
     title = "Command Overview"
     description = (
         "Use `{0}help command` for more information about a specific command "
         + " or go to the next page").format(
             config.Config.instance().command_prefix)
     entries = []
     for k, cmd_list in cmd_dict.items():
         for cmd in cmd_dict[k]['cmd_list']:
             entries.append(
                 Entry(
                     f"{config.Config.instance().command_prefix}{cmd.triggers[0]}",
                     cmd.overview))
     if adminhelp:
         entries.append(
             Entry(f"{config.Config.instance().command_prefix}adminhelp",
                   "View the full list of admin commands"))
     pages.append(
         Page(entries=entries,
              title=title,
              author=author,
              description=description))
     # Build detail pages
     for group, details in cmd_dict.items():
         author = cmd_dict[group]['header']
         description = cmd_dict[group]['info']
         entries = self.get_entries(cmd_dict[group]['cmd_list'])
         pages.append(
             Page(entries=entries, author=author, description=description))
     # Info
     entries = [
         Entry(
             f"{config.Config.instance().command_prefix}{tips.TIPAUTHOR_INFO.triggers[0]}",
             tips.TIPAUTHOR_INFO.details)
     ]
     author = f"Graham v{__version__} for {Env.currency_name()}"
     heart = '\U0001F49B' if Env.banano() else '\U0001F499'
     description = "This bot is completely free, open source, and MIT licnesed"
     description += f"\n\nMade with {heart} for the **BANANO** and **NANO** communities"
     description += f"\nHangout with some awesome people at https://chat.banano.cc"
     description += f"\nMy Discord: **@bbedward#9246**"
     description += f"\nMy Reddit: **/u/bbedward**"
     description += f"\nMy Twitter: **@theRealBbedward**"
     description += f"\n\nGraham GitHub: https://github.com/bbedward/graham_discord_bot"
     pages.append(
         Page(entries=entries, author=author, description=description))
     return pages
Exemplo n.º 6
0
 def format_balance_message(self, balance_raw: int, pending_raw: int,
                            pending_send_db: int,
                            pending_receive_db: int) -> discord.Embed:
     embed = discord.Embed(
         colour=0xFBDD11 if Env.banano() else discord.Colour.dark_blue())
     embed.set_author(
         name="Balance",
         icon_url=
         "https://github.com/bbedward/graham_discord_bot/raw/master/assets/banano_logo.png"
         if Env.banano() else
         "https://github.com/bbedward/graham_discord_bot/raw/master/assets/nano_logo.png"
     )
     embed.description = "**Available:**\n"
     embed.description += f"```{Env.commafy(Env.format_float(Env.raw_to_amount(balance_raw - pending_send_db)))} {Env.currency_symbol()}\n"
     pending_receive_str = f"+ {Env.commafy(Env.format_float(Env.raw_to_amount(pending_raw + pending_receive_db)))} {Env.currency_symbol()}"
     pending_send_str = f"- {Env.commafy(Env.format_float(Env.raw_to_amount(pending_send_db)))} {Env.currency_symbol()}"
     rjust_size = max(len(pending_send_str), len(pending_receive_str))
     embed.description += f"{pending_receive_str.ljust(rjust_size)} (Pending Receipt)\n{pending_send_str.ljust(rjust_size)} (Pending Send)```\n"
     embed.set_footer(
         text=
         "Pending balances are in queue and will become available after processing."
     )
     return embed
Exemplo n.º 7
0
    async def sendmax_cmd(self, ctx: Context):
        if ctx.error:
            return

        msg = ctx.message

        user: User = ctx.user
        destination: str = ctx.destination

        bal = await user.get_available_balance_dec()
        if (bal < 0.01 and Env.banano()) or (bal < 0.000001 and not Env.banano()):
            await Messages.send_error_dm(msg.author, "You balance is 0, so I can't make any withdraw")
            return

        # Create transaction
        tx = await Transaction.create_transaction_external(
            sending_user=user,
            amount=await user.get_available_balance_dec(),
            destination=destination
        )
        # Queue the actual send
        await TransactionQueue.instance().put(tx)
        # Send user message
        await Messages.send_success_dm(msg.author, "I've queued your transaction! I'll let you know once I broadcast it to the network.")
Exemplo n.º 8
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
Exemplo n.º 9
0
 async def callback(self, request: web.Request):
     """Route for handling HTTP callback"""
     request_json = await request.json()
     hash = request_json['hash']
     self.logger.debug(f"callback received {hash}")
     # cache
     if not await RedisDB.instance().exists(f"callback:{hash}"):
         await RedisDB.instance().set(f"callback:{hash}",
                                      "val",
                                      expires=300)
     else:
         return web.HTTPOk()
     # De-serialize block
     request_json['block'] = json.loads(request_json['block'])
     # only consider sends
     if 'is_send' in request_json and (request_json['is_send'] or
                                       request_json['is_send'] == 'true'):
         if 'amount' in request_json:
             # only consider self.min_amount or larger
             converted_amount = Env.raw_to_amount(
                 int(request_json['amount']))
             if converted_amount >= self.min_amount:
                 # Figure out of this is one of our users
                 link = request_json['block']['link_as_account']
                 account = await Account.filter(
                     address=link).prefetch_related('user').first()
                 if account is None:
                     return web.HTTPOk()
                 # See if this is an internal TX
                 transaction = await Transaction.filter(
                     block_hash=hash
                 ).prefetch_related('receiving_user').first()
                 if transaction is not None and transaction.receiving_user is not None:
                     return web.HTTPOk()
                 self.logger.debug(
                     f'Deposit received: {request_json["amount"]} for {account.user.id}'
                 )
                 amount_string = f"{Env.raw_to_amount(int(request_json['amount']))} {Env.currency_symbol()}"
                 discord_user = await self.bot.fetch_user(account.user.id)
                 if discord_user is not None:
                     await Messages.send_success_dm(
                         discord_user,
                         f"Your deposit of **{amount_string}** has been received. It will be in your available balance shortly!",
                         header="Deposit Success",
                         footer=
                         f"I only notify you of deposits that are {self.min_amount} {Env.currency_symbol()} or greater."
                     )
     return web.HTTPOk()
Exemplo n.º 10
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
Exemplo n.º 11
0
 def __init__(self, bot: Bot, host: str, port: int):
     self.bot = bot
     self.app = web.Application(middlewares=[web.normalize_path_middleware()])
     self.app.add_routes([
         web.post('/callback', self.callback)
     ])
     cors = aiohttp_cors.setup(self.app, defaults={
         "*": aiohttp_cors.ResourceOptions(
                 allow_credentials=True,
                 expose_headers="*",
                 allow_headers="*",
             )
     })
     ufw_resource = cors.add(self.app.router.add_resource("/ufw/{wallet}"))
     cors.add(ufw_resource.add_route("GET", self.ufw)) 
     wfu_resource = cors.add(self.app.router.add_resource("/wfu/{user}"))
     cors.add(wfu_resource.add_route("GET", self.wfu))
     users_resource = cors.add(self.app.router.add_resource("/users"))
     cors.add(users_resource.add_route("GET", self.users))
     self.logger = logging.getLogger()
     self.host = host
     self.port = port
     self.min_amount = 10 if Env.banano() else 0.1
Exemplo n.º 12
0
 def test_format_float(self):
     self.assertEqual(Env.format_float(9.90000), "9.9")
     self.assertEqual(Env.format_float(9.0), "9")
     self.assertEqual(Env.format_float(9), "9")
     self.assertEqual(Env.format_float(9.9000010), "9.900001")
Exemplo n.º 13
0
import logging
from util.regex import AmountAmbiguousException, AmountMissingException, RegexUtil
from util.validators import Validators
from util.util import Utils
from util.discord.channel import ChannelUtil
from util.discord.messages import Messages
from db.models.user import User
from db.redis import RedisDB
from typing import List
from models.constants import Constants
from db.models.transaction import Transaction
from tasks.transaction_queue import TransactionQueue

# Commands Documentation
RAIN_INFO = CommandInfo(
    triggers=["brain" if Env.banano() else "nrain", "brian", "nrian"],
    overview="Distribute a tip amount amongst active users",
    details="Distribute amount amongst active users." +
    f"\nExample: `{config.Config.instance().command_prefix}{'b' if Env.banano() else 'n'}rain 1000` will distribute 1000 {Env.currency_symbol()} between everyone who is active."
    +
    f"\n **minimum amount to rain: {config.Config.instance().get_rain_minimum()} {Env.currency_symbol()}**"
)


class RainCog(commands.Cog):
    def __init__(self, bot: Bot):
        self.bot = bot
        self.logger = logging.getLogger()

    @commands.Cog.listener()
    async def on_message(self, message: discord.Message):
Exemplo n.º 14
0
    async def ticketstatus_cmd(self, ctx: Context):
        if ctx.error:
            return

        msg = ctx.message
        user = ctx.user
        author = msg.author
        content = msg.content

        # If private, see what servers they are part of
        guilds = None
        if ChannelUtil.is_private(msg.channel):
            guilds = []
            for g in self.bot.guilds:
                if g.get_member(msg.author.id) is not None:
                    guilds.append(g)
            if len(guilds) == 0:
                return

        # See if they've been spamming
        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)
                if spam >= 3:
                    await Messages.send_error_dm(msg.author, "You're temporarily banned from entering giveaways")
                    await Messages.delete_message(msg)
                    return
            else:
                spam = 0
        else:
            spam = 0

        # Get active giveaway(s) - public channel
        if guilds == None:
            gw = await Giveaway.get_active_giveaway(server_id=msg.guild.id)

            if gw is None:
                await Messages.send_error_dm(msg.author, "There aren't any active giveaways.")
                await Messages.delete_message(msg)
                # Block ticket spam
                await RedisDB.instance().set(f"ticketspam:{msg.author.id}", str(spam + 1), expires=3600)
                return

            # Get their active giveaway transaction
            active_tx = await Transaction.filter(giveaway__id=gw.id, sending_user__id=user.id).first()
            response = None
            if active_tx is None:
                if int(gw.entry_fee) > 0:
                    fee_converted = Env.raw_to_amount(int(gw.entry_fee))
                    response = f"There is a fee of **{fee_converted} {Env.currency_symbol()}**!\n"
                    response+= f"Use `{config.Config.instance().command_prefix}ticket {fee_converted}` to pay the fee and enter"
                else:
                    response = f"This giveaway is free to enter\n"
                    response+= f"Use `{config.Config.instance().command_prefix}ticket` to enter."
            else:
                needed = int(gw.entry_fee) - int(active_tx.amount)
                if needed <= 0:
                    response = f"You're already entered into this giveaway"
                else:
                    fee_converted = Env.raw_to_amount(int(gw.entry_fee))
                    paid_converted = Env.raw_to_amount(int(active_tx.amount))
                    response = f"There is a fee of **{fee_converted} {Env.currency_symbol()}**! You've donated **{paid_converted} {Env.currency_symbol()}** but that's not enough to enter!\n"
                    response+= f"Use `{config.Config.instance().command_prefix}ticket {NumberUtil.format_float(fee_converted - paid_converted)}` to pay the fee and enter"

            # Build response
            embed = discord.Embed(colour=0xFBDD11 if Env.banano() else discord.Colour.dark_blue())
            embed.set_author(name=f"Giveaway #{gw.id} is active!", icon_url="https://github.com/bbedward/graham_discord_bot/raw/master/assets/banano_logo.png" if Env.banano() else "https://github.com/bbedward/graham_discord_bot/raw/master/assets/nano_logo.png")
            embed.description = response

            await msg.author.send(embed=embed)
            await Messages.delete_message(msg)
            return
        # Get active giveaways (private channel)
        gws = await Giveaway.get_active_giveaways(server_ids=[g.id for g in guilds])
        if gws is None or len(gws) == 0:
            await Messages.send_error_dm(msg.author, "There aren't any active giveaways.")
            await Messages.delete_message(msg)
            # Block ticket spam
            await RedisDB.instance().set(f"ticketspam:{msg.author.id}", str(spam + 1), expires=3600)
            return

        # Get their active giveaway transaction
        response = None
        for gw in gws:
            active_tx = await Transaction.filter(giveaway__id=gw.id, sending_user__id=user.id).first()
            response = f"**Giveaway #{gw.id}**\n" if response is None else f"**Giveaway #{gw.id}**:\n"
            if active_tx is None:
                if int(gw.entry_fee) > 0:
                    fee_converted = Env.raw_to_amount(int(gw.entry_fee))
                    response+= f"There is a fee of **{fee_converted} {Env.currency_symbol()}**!\n"
                    response+= f"Use `{config.Config.instance().command_prefix}ticket {fee_converted} id={gw.id}` to pay the fee and enter\n"
                else:
                    response+= f"This giveaway is free to enter\n"
                    response+= f"Use `{config.Config.instance().command_prefix}ticket id={gw.id}` to enter.\n"
            else:
                needed = int(gw.entry_fee) - int(active_tx.amount)
                if needed <= 0:
                    response+= f"You're already entered into this giveaway"
                else:
                    fee_converted = Env.raw_to_amount(int(gw.entry_fee))
                    paid_converted = Env.raw_to_amount(int(active_tx.amount))
                    response+= f"There is a fee of **{fee_converted} {Env.currency_symbol()}**! You've donated **{paid_converted} {Env.currency_symbol()}** but that's not enough to enter!\n"
                    response+= f"Use `{config.Config.instance().command_prefix}ticket {NumberUtil.format_float(fee_converted - paid_converted)} id={gw.id}` to pay the fee and enter\n"

        # Build response
        embed = discord.Embed(colour=0xFBDD11 if Env.banano() else discord.Colour.dark_blue())
        embed.set_author(name=f"Here are the active giveaways!", icon_url="https://github.com/bbedward/graham_discord_bot/raw/master/assets/banano_logo.png" if Env.banano() else "https://github.com/bbedward/graham_discord_bot/raw/master/assets/nano_logo.png")
        embed.description = response

        await msg.author.send(embed=embed)
        await Messages.delete_message(msg)
Exemplo n.º 15
0
from util.regex import AmountAmbiguousException, AmountMissingException, RegexUtil
from util.validators import Validators
from util.util import Utils
from util.discord.channel import ChannelUtil
from util.discord.messages import Messages
from db.models.user import User
from db.redis import RedisDB
from typing import List
from models.constants import Constants
from util.number import NumberUtil
from db.models.transaction import Transaction
from tasks.transaction_queue import TransactionQueue

# Commands Documentation
RAIN_INFO = CommandInfo(
    triggers=["brain" if Env.banano() else "rain"],
    overview="Distribute a tip amount amongst active users",
    details="Distribute amount amongst active users." +
    f"\nExample: `{config.Config.instance().command_prefix}rain 100` will distribute 100 {Env.currency_symbol()} between everyone who is active."
    +
    f"\n **minimum amount to rain: {config.Config.instance().get_rain_minimum()} {Env.currency_symbol()}**"
)


class RainCog(commands.Cog):
    def __init__(self, bot: Bot):
        self.bot = bot
        self.logger = logging.getLogger()

    @commands.Cog.listener()
    async def on_message(self, message: discord.Message):
Exemplo n.º 16
0
    async def winners_cmd(self, ctx: Context):
        if ctx.error:
            return

        msg = ctx.message
        user = ctx.user

        as_dm = False

        # 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 giveaway stats in this channel")
                await RedisDB.instance().set(f"ticketspam:{msg.author.id}", str(spam + 1), expires=3600)
                return

        if not as_dm:
            # Check spamming of this command
            if await RedisDB.instance().exists(f'winnersspam:{msg.channel.id}'):
                as_dm = True
            else:
                await RedisDB.instance().set(f'winnersspam:{msg.channel.id}', 'as', expires=60)

        # Get list
        winners = await Giveaway.filter(server_id=msg.guild.id, winning_user_id__not_isnull=True, ended_at__not_isnull=True).order_by('-ended_at').prefetch_related('winning_user').limit(10).all()

        if len(winners) == 0:
            await msg.add_x_reaction(msg)
            await Messages.send_error_dm(msg.author, "There haven't been any giveaways on this server yet")
            return

        response_msg = "```"
        # Get biggest amount to adjust the padding
        biggest_num = 0
        for winner in winners:
            winning_amount = Env.raw_to_amount(int(winner.final_amount))
            length = len(f"{NumberUtil.format_float(winning_amount)} {Env.currency_symbol()}")
            if length > biggest_num:
                biggest_num = length
        for rank, winner in enumerate(winners, start=1):
            adj_rank = str(rank) if rank >= 10 else f" {rank}"
            user_name = winner.winning_user.name
            amount_str = f"{NumberUtil.format_float(Env.raw_to_amount(int(winner.final_amount)))} {Env.currency_symbol()}".ljust(biggest_num)
            response_msg += f"{adj_rank}. {amount_str} - won by {user_name}\n" 
        response_msg += "```"

        embed = discord.Embed(colour=0xFBDD11 if Env.banano() else discord.Colour.dark_blue())
        embed.set_author(name=f"Here are the last {len(winners)} giveaway winners \U0001F44F", icon_url="https://github.com/bbedward/graham_discord_bot/raw/master/assets/banano_logo.png" if Env.banano() else "https://github.com/bbedward/graham_discord_bot/raw/master/assets/nano_logo.png")
        embed.description = response_msg

        if not as_dm:
            await msg.channel.send(f"<@{msg.author.id}>", embed=embed)    
        else:
            await msg.author.send(embed=embed)
            await msg.add_reaction('\u2709')
Exemplo n.º 17
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)
Exemplo n.º 18
0
    async def ticket_cmd(self, ctx: Context):
        if ctx.error:
            return

        msg = ctx.message
        user = ctx.user
        author = msg.author
        content = msg.content

        is_private = ChannelUtil.is_private(msg.channel)
        id=None

        if is_private:
            if 'id=' not in msg.content:
                await Messages.send_usage_dm(msg.author, TICKET_INFO)
                await Messages.add_x_reaction(msg)
                return            

            # Parse message
            split_content = msg.content.split(' ')
            cleaned_content = msg.content
            for split in split_content:
                if split.startswith('id='):
                    cleaned_content.replace(split, "")
                    split = split.replace('id=','').strip()
                    if not split:
                        continue
                    try:
                        id = int(split)
                    except ValueError as e:
                        await Messages.add_x_reaction(msg)
                        await Messages.send_usage_dm(msg.author, TICKET_INFO)
                        return

        # See if they've been spamming
        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)
                if spam >= 3:
                    await Messages.send_error_dm(msg.author, "You're temporarily banned from entering giveaways")
                    await Messages.delete_message_if_ok(msg)
                    return
            else:
                spam = 0
        else:
            spam = 0

        # Get active giveaway
        if id is None:
            gw = await Giveaway.get_active_giveaway(server_id=msg.guild.id)
        else:
            gw = await Giveaway.get_active_giveaway_by_id(id=id)

        if gw is None:
            await Messages.send_error_dm(msg.author, "There aren't any active giveaways to enter.")
            await Messages.delete_message_if_ok(msg)
            # Block ticket spam
            await RedisDB.instance().set(f"ticketspam:{msg.author.id}", str(spam + 1), expires=3600)
            return

        # Check roles
        if is_private:
            guild = self.bot.get_guild(gw.server_id)
            if guild is None:
                await Messages.send_error_dm(msg.author, "Something went wrong, ask my master for help")
                return
            member = guild.get_member(msg.author.id)
            if member is None:
                await "You're not a member of that server"
                return
            msg.author = member
    
        if not await self.role_check(msg):
            return

        # There is an active giveaway, enter em if not already entered.
        active_tx = await Transaction.filter(giveaway__id=gw.id, sending_user__id=user.id).first()
        if active_tx is not None and int(gw.entry_fee) == 0:
            await Messages.send_error_dm(msg.author, "You've already entered this giveaway.")
            await Messages.delete_message_if_ok(msg)
            return
        elif active_tx is None:
            paid_already = 0
        else:
            paid_already = int(active_tx.amount)
    
        if paid_already >= int(gw.entry_fee) and int(gw.entry_fee) > 0:
            await Messages.send_error_dm(msg.author, "You've already entered this giveaway.")
            await Messages.delete_message_if_ok(msg)
            return

        # Enter em
        fee_raw = int(gw.entry_fee) - paid_already
        fee = Env.raw_to_amount(fee_raw)
        # Check balance if fee is > 0
        if fee > 0:
            try:
                amount = RegexUtil.find_float(msg.content)
                if amount < fee:
                    await Messages.send_error_dm(msg.author, f"This giveaway has a fee of {fee} {Env.currency_symbol()}. The amount you specified isn't enough to cover the entry fee")
                    await Messages.delete_message_if_ok(msg)
                    return
            except AmountMissingException:
                await Messages.send_error_dm(msg.author, f"This giveaway has a fee, you need to specify the amount to enter. `{config.Config.instance().command_prefix}ticket {fee}`")
                await Messages.delete_message_if_ok(msg)
                return
            available_balance = Env.raw_to_amount(await user.get_available_balance())
            if fee > available_balance:
                await Messages.add_x_reaction(ctx.message)
                await Messages.send_error_dm(msg.author, f"Your balance isn't high enough to complete this tip. You have **{available_balance} {Env.currency_symbol()}**, but this entry would cost you **{fee} {Env.currency_symbol()}**")
                await Messages.delete_message_if_ok(msg)
                await RedisDB.instance().set(f"ticketspam:{msg.author.id}", str(spam + 1), expires=3600)
                return
        await Transaction.create_transaction_giveaway(
            user,
            fee,
            gw
        )
        await Messages.send_success_dm(msg.author, f"You've successfully been entered into giveaway #{gw.id}")
        await Messages.delete_message_if_ok(msg)
        return
Exemplo n.º 19
0
    async def tiprandom_cmd(self, ctx: Context):
        if ctx.error:
            await Messages.add_x_reaction(ctx.message)
            return

        msg = ctx.message
        user = ctx.user
        send_amount = ctx.send_amount

        # Check anti-spam
        if not ctx.god and await RedisDB.instance().exists(
                f"tiprandomspam{msg.guild.id}{msg.author.id}"):
            await Messages.add_timer_reaction(msg)
            await Messages.send_basic_dm(
                msg.author, "You can only tiprandom once every minute")
            return

        active_users = await rain.RainCog.get_active(ctx,
                                                     excluding=msg.author.id)

        if len(active_users) < Constants.RAIN_MIN_ACTIVE_COUNT:
            await Messages.send_error_dm(
                msg.author,
                f"There aren't enough active people to do a random tip. Only **{len(active_users)}** are active, but I'd like to see at least **{Constants.RAIN_MIN_ACTIVE_COUNT}**"
            )
            return

        target_user = secrets.choice(active_users)

        # See how much they need to make this tip.
        available_balance = Env.raw_to_amount(await
                                              user.get_available_balance())
        if send_amount > available_balance:
            await Messages.add_x_reaction(ctx.message)
            await Messages.send_error_dm(
                msg.author,
                f"Your balance isn't high enough to complete this tip. You have **{available_balance} {Env.currency_symbol()}**, but this tip would cost you **{send_amount} {Env.currency_symbol()}**"
            )
            return

        # Make the transactions in the database
        tx = await Transaction.create_transaction_internal_dbuser(
            sending_user=user, amount=send_amount, receiving_user=target_user)
        task_list = []
        if not await user.is_muted_by(target_user.id):
            task_list.append(
                Messages.send_basic_dm(
                    member=msg.guild.get_member(target_user.id),
                    message=
                    f"You were randomly selected and received **{send_amount} {Env.currency_symbol()}** from {msg.author.name.replace('`', '')}.\nUse `{config.Config.instance().command_prefix}mute {msg.author.id}` to disable notifications for this user.",
                    skip_dnd=True))
        task_list.append(
            Messages.send_basic_dm(
                member=msg.author,
                message=
                f'"{target_user.name}" was the recipient of your random tip of {send_amount} {Env.currency_symbol()}'
            ))
        asyncio.ensure_future(Utils.run_task_list(task_list))
        # Add reactions
        await Messages.add_tip_reaction(msg, send_amount)
        # Queue the actual send
        await TransactionQueue.instance().put(tx)
        # anti spam
        await RedisDB.instance().set(
            f"tiprandomspam{msg.guild.id}{msg.author.id}", "as", expires=60)
        # 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(send_amount)
Exemplo n.º 20
0
    async def start_giveaway_timer(self, giveaway: Giveaway):
        # Ensure timer not already started
        if giveaway.id in self.giveaway_ids:
            return
        self.giveaway_ids.append(giveaway.id)
        # Sleep for <giveaway duration> seconds
        delta = (giveaway.end_at - datetime.datetime.utcnow()).total_seconds()
        if delta > 0:
            await asyncio.sleep(delta)
        # End the giveaway
        # Get entries
        txs = await Transaction.filter(giveaway=giveaway).prefetch_related('sending_user').all()
        users = []
        for tx in txs:
            if tx.sending_user not in users and int(tx.amount) >= int(giveaway.entry_fee):
                users.append(tx.sending_user)
        # Pick winner
        random.shuffle(users, Utils.random_float)
        winner = secrets.choice(users)
        # Calculate total winning amount
        tx_sum = 0
        for tx in txs:
            tx_sum += int(tx.amount)
        # Finish this
        async with in_transaction() as conn:
            giveaway.ended_at = datetime.datetime.utcnow()
            giveaway.winning_user = winner
            giveaway.final_amount = str(tx_sum)
            await giveaway.save(using_db=conn, update_fields=['ended_at', 'winning_user_id', 'final_amount'])
            # Update transactions
            winner_account = await winner.get_address()
            for tx in txs:
                if tx.amount == '0':
                    await tx.delete()
                else:
                    tx.destination = winner_account
                    tx.receiving_user = winner
                    await tx.save(using_db=conn, update_fields=['receiving_user_id', 'destination'])
        # Queue transactions
        for tx in txs:
            await TransactionQueue.instance().put(tx)
        # Announce winner
        main_channel = self.bot.get_channel(giveaway.started_in_channel)
        announce_channels = []
        if main_channel is not None:
            announce_channels.append(main_channel)
        for ch in config.Config.instance().get_giveaway_announce_channels():
            if ch == giveaway.started_in_channel:
                continue
            dch = self.bot.get_channel(ch)
            if dch is not None:
                announce_channels.append(dch)

        ann_message = f"Congratulations! <@{winner.id}> was the winner of the giveaway!"
        ann_message+= f"\nThey have been sent **{Env.raw_to_amount(tx_sum)} {Env.currency_symbol()}**"
        if isinstance(giveaway.started_by, User):
            ann_message+= f"\n\nThanks to <@{giveaway.started_by.id}> for sponsoring this giveaway!"
        embed = discord.Embed(colour=0xFBDD11 if Env.banano() else discord.Colour.dark_blue())
        embed.set_author(name="We have a winner!", icon_url="https://github.com/bbedward/graham_discord_bot/raw/master/assets/banano_logo.png" if Env.banano() else "https://github.com/bbedward/graham_discord_bot/raw/master/assets/nano_logo.png")
        embed.description = ann_message

        for ann in announce_channels:
            try:
                await ann.send(embed=embed)
            except Exception:
                pass
        # DM the winner
        member = self.bot.get_user(winner.id)
        if member is not None:
            await Messages.send_success_dm(member, f"Congratulations! **You've won giveaway #{giveaway.id}**! I've sent you **{Env.raw_to_amount(tx_sum)} {Env.currency_symbol()}**")
        # Cleanup
        try:
            self.giveaway_ids.remove(giveaway.id)
        except ValueError:
            pass
Exemplo n.º 21
0
    async def giveaway_cmd(self, ctx: Context):
        if ctx.error:
            return

        msg = ctx.message
        user = ctx.user

        # Check paused
        if await RedisDB.instance().is_paused():
            await Messages.send_error_dm(msg.author, f"Transaction activity is currently suspended. I'll be back online soon!")
            return

        # Check roles
        if not await self.role_check(msg):
            return
        elif msg.channel.id in config.Config.instance().get_no_spam_channels():
            await Messages.send_error_dm(msg.author, f"You can't start giveaways in this channel")
            return

        if 'fee=' not in msg.content or 'duration=' not in msg.content:
            await Messages.send_usage_dm(msg.author, START_GIVEAWAY_INFO)
            await Messages.add_x_reaction(msg)
            return

        # Parse message
        split_content = msg.content.split(' ')
        cleaned_content = msg.content
        for split in split_content:
            if split.startswith('fee='):
                cleaned_content.replace(split, "")
                split = split.replace('fee=','').strip()
                if not split:
                    continue
                try:
                    fee = abs(float(split))
                except ValueError as e:
                    await Messages.add_x_reaction(msg)
                    await Messages.send_usage_dm(msg.author, START_GIVEAWAY_INFO)
                    return
            elif split.startswith('duration='):
                cleaned_content.replace(split, "")
                split=split.replace('duration=','').strip()
                if not split:
                    continue
                try:
                    duration = abs(int(split))
                    if not ctx.god and (duration < config.Config.instance().get_giveaway_min_duration() or duration > config.Config.instance().get_giveaway_max_duration()):
                        raise ValueError("Bad duration specified")
                except ValueError as e:
                    await Messages.add_x_reaction(msg)
                    await Messages.send_usage_dm(msg.author, START_GIVEAWAY_INFO)
                    return
        # Find giveaway amount
        try:
            giveaway_amount = RegexUtil.find_float(cleaned_content)
            if Validators.too_many_decimals(giveaway_amount):
                await Messages.send_error_dm(ctx.message.author, f"You are only allowed to use {Env.precision_digits()} digits after the decimal for giveaway amount.")
                ctx.error = True
                return
            elif fee > giveaway_amount * config.Config.instance().get_giveaway_max_fee_multiplier():
                await Messages.add_x_reaction(msg)
                await Messages.send_usage_dm(msg.author, START_GIVEAWAY_INFO)
                return
            elif giveaway_amount < config.Config.instance().get_giveaway_minimum():
                await Messages.add_x_reaction(msg)
                await Messages.send_usage_dm(msg.author, START_GIVEAWAY_INFO)
                return                
        except AmountMissingException:
            await Messages.add_x_reaction(msg)
            await Messages.send_usage_dm(msg.author, START_GIVEAWAY_INFO)
            return

        # See how much they need to make this tip.
        available_balance = Env.raw_to_amount(await user.get_available_balance())
        if giveaway_amount > available_balance:
            await Messages.add_x_reaction(ctx.message)
            await Messages.send_error_dm(msg.author, f"Your balance isn't high enough to start this giveaway. You have **{available_balance} {Env.currency_symbol()}**, but this tip would cost you **{giveaway_amount} {Env.currency_symbol()}**")
            return

        try:
            # 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
                active_giveaway = await Giveaway.get_active_giveaway(server_id=msg.guild.id)
                if active_giveaway is not None:
                    await Messages.add_x_reaction(msg)
                    await Messages.send_error_dm(msg.author, "There's already a giveaway in progress on this server")
                    return
                # Start giveaway
                async with in_transaction() as conn:
                    gw = await Giveaway.start_giveaway_user(
                        server_id=msg.guild.id,
                        started_by=user,
                        amount=giveaway_amount,
                        entry_fee=fee,
                        duration=duration,
                        started_in_channel=msg.channel.id,
                        conn=conn
                    )
                    # Create pending TX for this user
                    await Transaction.create_transaction_giveaway(
                        sending_user=user,
                        amount=giveaway_amount,
                        giveaway=gw,
                        conn=conn
                    )
                # Announce giveaway
                embed = self.format_giveaway_announcement(gw)
                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))
        except LockTimeoutError:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(msg.author, "I couldn't start a giveaway, maybe someone else beat you to it as there can only be 1 active at a time.")
Exemplo n.º 22
0
 async def add_tip_reaction(msg: discord.Message,
                            amount: float,
                            rain: bool = False):
     if Env.banano():
         if amount > 0:
             try:
                 await msg.add_reaction('\:tip:425878628119871488'
                                        )  # TIP mark
                 await msg.add_reaction('\:tick:425880814266351626'
                                        )  # check mark
             except Exception:
                 await msg.add_reaction('\U00002611'
                                        )  # fallback for non-banano server
         if amount > 0 and amount < 50:
             await msg.add_reaction('\U0001F987')  # S
         elif amount >= 50 and amount < 250:
             await msg.add_reaction('\U0001F412')  # C
         elif amount >= 250:
             await msg.add_reaction('\U0001F98D')  # W
         if rain:
             try:
                 await msg.add_reaction('\:bananorain:430826677543895050'
                                        )  # Banano rain
             except Exception:
                 await msg.add_reaction('\U0001F4A6')  # Sweat Drops
     else:
         if amount > 0:
             await msg.add_reaction('\U00002611')  # check mark
         if amount > 0 and amount < 0.01:
             await msg.add_reaction('\U0001F1F8')  # S
             await msg.add_reaction('\U0001F1ED')  # H
             await msg.add_reaction('\U0001F1F7')  # R
             await msg.add_reaction('\U0001F1EE')  # I
             await msg.add_reaction('\U0001F1F2')  # M
             await msg.add_reaction('\U0001F1F5')  # P
         elif amount >= 0.01 and amount < 0.1:
             await msg.add_reaction('\U0001F1E8')  # C
             await msg.add_reaction('\U0001F1F7')  # R
             await msg.add_reaction('\U0001F1E6')  # A
             await msg.add_reaction('\U0001F1E7')  # B
         elif amount >= 0.1 and amount < 0.5:
             await msg.add_reaction('\U0001F1FC')  # W
             await msg.add_reaction('\U0001F1E6')  # A
             await msg.add_reaction('\U0001F1F1')  # L
             await msg.add_reaction('\U0001F1F7')  # R
             await msg.add_reaction('\U0001F1FA')  # U
             await msg.add_reaction('\U0001F1F8')  # S
         elif amount >= 0.5 and amount < 1:
             await msg.add_reaction('\U0001F1F8')  # S
             await msg.add_reaction('\U0001F1ED')  # H
             await msg.add_reaction('\U0001F1E6')  # A
             await msg.add_reaction('\U0001F1F7')  # R
             await msg.add_reaction('\U0001F1F0')  # K
         elif amount >= 1:
             await msg.add_reaction('\U0001F1F2')  # M
             await msg.add_reaction('\U0001F1EA')  # E
             await msg.add_reaction('\U0001F1EC')  # G
             await msg.add_reaction('\U0001F1E6')  # A
             await msg.add_reaction('\U0001F1F1')  # L
             await msg.add_reaction('\U0001F1E9')  # D
             await msg.add_reaction('\U0001F1F4')  # O
             await msg.add_reaction('\U0001F1F3')  # N
         if rain:
             await msg.add_reaction('\U0001F4A6')  # Sweat Drops
Exemplo n.º 23
0
    triggers = ["addfavorite"],
    overview = "Add a user to your favorites list",
    details = f"Add a user to your favorites list. You can have up to **25 favorites**. Example: `{config.Config.instance().command_prefix}addfavorite @bbedward`"
)
REMOVE_FAVORITE_INFO = CommandInfo(
    triggers = ["unfavorite", "removefavorite"],
    overview = "Remove a user from your favorites list",
    details = f"Remove a user from your favorites list Example: `{config.Config.instance().command_prefix}removefavorite 419483863115366410`"
)
FAVORITES_INFO = CommandInfo(
    triggers = ["favorites"],
    overview = "View list of users you have favorited",
    details = f"View the list of every user you have favorited. You can tip all of them using `{config.Config.instance().command_prefix}{'banfavorites' if Env.banano() else 'ntipfavorites'} <amount>`"
)
TIPFAVORITES_INFO = CommandInfo(
    triggers = ["banfavorites" if Env.banano() else "ntipfavorites"],
    overview = "Tip all the favorites",
    details = f"Split a tip among all of the users in your favorites list - similar to a tipsplit. (**minimum tip is {Constants.TIP_MINIMUM} {Constants.TIP_UNIT}**)" +
                f"\nExample: `{config.Config.instance().command_prefix}{'banfavorites' if Env.banano() else 'ntipfavorites'} <amount>`"
)

class FavoriteCog(commands.Cog):
    """Commands for admins only"""
    def __init__(self, bot: Bot):
        self.bot = bot

    async def cog_before_invoke(self, ctx: Context):
        ctx.error = False
        msg = ctx.message
        # See if user exists in DB
        user = await User.get_user(msg.author)
Exemplo n.º 24
0
from util.validators import Validators
from db.models.stats import Stats
from db.models.transaction import Transaction
from db.models.user import User
from db.redis import RedisDB
from tasks.transaction_queue import TransactionQueue

import asyncio
import config
import cogs.rain as rain
import secrets
from util.util import Utils

## Command documentation
TIP_INFO = CommandInfo(
    triggers=["ban", "b"] if Env.banano() else ["ntip", "n"],
    overview="Send a tip to mentioned users",
    details=
    f"Tip specified amount to mentioned user(s) (**minimum tip is {Constants.TIP_MINIMUM} {Constants.TIP_UNIT}**)"
    + "\nThe recipient(s) will be notified of your tip via private message" +
    "\nSuccessful tips will be deducted from your available balance immediately.\n"
    +
    f"Example: `{config.Config.instance().command_prefix}{'ban' if Env.banano() else 'ntip'} 2 @user1 @user2` would send 2 to user1 and 2 to user2"
)
TIPSPLIT_INFO = CommandInfo(
    triggers=["bansplit", "bs"] if Env.banano() else ["ntipsplit", "ns"],
    overview="Split a tip among mentioned users",
    details=
    f"Divide the specified amount between mentioned user(s) (**minimum tip is {Constants.TIP_MINIMUM} {Constants.TIP_UNIT}**)"
    + "\nThe recipient(s) will be notified of your tip via private message" +
    "\nSuccessful tips will be deducted from your available balance immediately.\n"
Exemplo n.º 25
0
    async def rain_cmd(self, ctx: Context):
        if ctx.error:
            return

        msg = ctx.message
        user = ctx.user
        send_amount = ctx.send_amount

        anon = 'anon' in msg.content

        # Get active users
        active_users = await self.get_active(ctx, excluding=msg.author.id)

        if len(active_users) < Constants.RAIN_MIN_ACTIVE_COUNT:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(
                msg.author,
                f"Not enough users are active to rain - I need at least {Constants.RAIN_MIN_ACTIVE_COUNT} but there's only {len(active_users)} active bros"
            )
            return

        individual_send_amount = Env.truncate_digits(
            send_amount / len(active_users), max_digits=Env.precision_digits())
        individual_send_amount_str = f"{individual_send_amount:.2f}" if Env.banano(
        ) else f"{individual_send_amount:.6f}"
        if individual_send_amount < Constants.TIP_MINIMUM:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(
                msg.author,
                f"Amount is too small to divide across {len(active_users)} users"
            )
            return

        # See how much they need to make this tip.
        amount_needed = Env.truncate_digits(individual_send_amount *
                                            len(active_users),
                                            max_digits=Env.precision_digits())
        available_balance = Env.raw_to_amount(await
                                              user.get_available_balance())
        if amount_needed > available_balance:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(
                msg.author,
                f"Your balance isn't high enough to complete this tip. You have **{available_balance} {Env.currency_symbol()}**, but this tip would cost you **{amount_needed} {Env.currency_symbol()}**"
            )
            return

        # Make the transactions in the database
        tx_list = []
        task_list = []
        for u in active_users:
            tx = await Transaction.create_transaction_internal_dbuser(
                sending_user=user,
                amount=individual_send_amount,
                receiving_user=u)
            tx_list.append(tx)
            if not await user.is_muted_by(u.id):
                if not anon:
                    task_list.append(
                        Messages.send_basic_dm(
                            member=msg.guild.get_member(u.id),
                            message=
                            f"You were tipped **{individual_send_amount_str} {Env.currency_symbol()}** by {msg.author.name.replace('`', '')}.\nUse `{config.Config.instance().command_prefix}mute {msg.author.id}` to disable notifications for this user.",
                            skip_dnd=True))
                else:
                    task_list.append(
                        Messages.send_basic_dm(
                            member=msg.guild.get_member(u.id),
                            message=
                            f"You were tipped **{individual_send_amount_str} {Env.currency_symbol()}** anonymously!",
                            skip_dnd=True))
        # Send DMs in the background
        asyncio.ensure_future(Utils.run_task_list(task_list))
        # Add reactions
        await Messages.add_tip_reaction(msg, amount_needed, rain=True)
        # Queue the actual sends
        for tx in tx_list:
            await TransactionQueue.instance().put(tx)
        # Add anti-spam
        await RedisDB.instance().set(f"rainspam{msg.author.id}",
                                     "as",
                                     expires=300)
        # 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(amount_needed)
        # DM creator
        await Messages.send_success_dm(
            msg.author,
            f"You rained **{amount_needed} {Env.currency_symbol()}** to **{len(tx_list)} users**, they received **{individual_send_amount_str} {Env.currency_symbol()}** each.",
            header="Make it Rain")
        # Make the rainer auto-rain eligible
        await self.auto_rain_eligible(msg)
Exemplo n.º 26
0
    async def giveawaystats_cmd(self, ctx: Context):
        if ctx.error:
            return

        msg = ctx.message
        user = ctx.user

        as_dm = False

        # 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 giveaway stats in this channel")
                await RedisDB.instance().set(f"ticketspam:{msg.author.id}", str(spam + 1), expires=3600)
                return

        if not as_dm:
            # Check spamming of this command
            if len(config.Config.instance().get_giveaway_no_delete_channels()) > 0 and msg.channel.id not in config.Config.instance().get_giveaway_no_delete_channels():
                as_dm = True
            if await RedisDB.instance().exists(f'giveawaystatsspam:{msg.channel.id}'):
                as_dm = True
            else:
                await RedisDB.instance().set(f'giveawaystatsspam:{msg.channel.id}', 'as', expires=60)

        gw = await Giveaway.get_active_giveaway(server_id=msg.guild.id)
        pending_gw = None
        if gw is None:
            pending_gw = await Giveaway.get_pending_bot_giveaway(server_id=msg.guild.id)
            if pending_gw is None:
                if as_dm:
                    await Messages.send_error_dm(msg.author, "There are no active giveaways")
                else:
                    await Messages.send_error_public(msg.channel, "There are no active giveaways")
                await Messages.delete_message_if_ok(msg)
                return
            else:
                gw = pending_gw

        # Get stats
        transactions = await gw.get_transactions()
        entries = 0
        donors = 0
        amount = 0
        for tx in transactions:
            tx: Transaction = tx
            if int(tx.amount) >= int(gw.entry_fee):
                entries +=  1
            donors += 1
            amount += Env.raw_to_amount(int(tx.amount))

        # Format stats message
        embed = discord.Embed(colour=0xFBDD11 if Env.banano() else discord.Colour.dark_blue())
        embed.set_author(name=f"Giveaway #{gw.id}", icon_url="https://github.com/bbedward/graham_discord_bot/raw/master/assets/banano_logo.png" if Env.banano() else "https://github.com/bbedward/graham_discord_bot/raw/master/assets/nano_logo.png")
        fee = Env.raw_to_amount(int(gw.entry_fee))
        if pending_gw is None:
            embed.description = f"There are **{entries} entries** to win **{NumberUtil.truncate_digits(amount, max_digits=Env.precision_digits())} {Env.currency_symbol()}**\n"
            if fee > 0:
                embed.description+= f"\nThis giveaway has an entry fee of **{fee} {Env.currency_name()}**"
                embed.description+= f"\n`{config.Config.instance().command_prefix}ticket {fee}` - To enter this giveaway"
                embed.description+= f"\n`{config.Config.instance().command_prefix}{'donate' if Env.banano() else 'ntipgiveaway'} <amount>` - To increase the pot"
            else:
                embed.description+= f"\nThis giveaway is free to enter:"
                embed.description+= f"\n`{config.Config.instance().command_prefix}ticket` - To enter this giveaway"
                embed.description+= f"\n`{config.Config.instance().command_prefix}{'donate' if Env.banano() else 'ntipgiveaway'} <amount>` - To increase the pot"            
            duration = (gw.end_at - datetime.datetime.utcnow()).total_seconds()
            if duration < 60:
                embed.description += f"\n\nThis giveaway will end in **{int(duration)} seconds**"
            else:
                duration = duration // 60
                embed.description += f"\n\nThis giveaway will end in **{int(duration)} minutes**"
            embed.description += "\nGood luck! \U0001F340"
        else:
            embed.description = f"This giveaway hasn't started yet\n"
            embed.description += f"\nSo far **{donors}** people have donated to this giveaway and **{entries}** people are eligible to win"
            embed.description += f"\n**{NumberUtil.truncate_digits(config.Config.instance().get_giveaway_auto_minimum() - amount, max_digits=Env.precision_digits())} {Env.currency_symbol()}** more needs to be donated to start this giveaway."

        try:
            if as_dm:
                await msg.author.send(embed=embed)
                if msg.channel.id in config.Config.instance().get_giveaway_no_delete_channels():
                    await msg.add_reaction('\u2709')
            else:
                await msg.channel.send(embed=embed)
        except Exception:
            pass
        await Messages.delete_message_if_ok(msg)
Exemplo n.º 27
0
def get_env(variable):
    return Env.get_env(variable)
Exemplo n.º 28
0
    async def tipsplit_cmd(self, ctx: Context):
        if ctx.error:
            await Messages.add_x_reaction(ctx.message)
            return

        msg = ctx.message
        user = ctx.user
        send_amount = ctx.send_amount

        # Get all eligible users to tip in their message
        users_to_tip = []
        for m in msg.mentions:
            if not m.bot and m.id != msg.author.id:
                users_to_tip.append(m)
        if len(users_to_tip) < 1:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(
                msg.author,
                f"No users you mentioned are eligible to receive tips.")
            return

        individual_send_amount = NumberUtil.truncate_digits(
            send_amount / len(users_to_tip), max_digits=Env.precision_digits())
        if individual_send_amount < Constants.TIP_MINIMUM:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(
                msg.author,
                f"Tip amount too small, each user needs to receive at least {Constants.TIP_MINIMUM}. With your tip they'd only be getting {individual_send_amount}"
            )
            return

        # See how much they need to make this tip.
        amount_needed = individual_send_amount * len(users_to_tip)
        available_balance = Env.raw_to_amount(await
                                              user.get_available_balance())
        if amount_needed > available_balance:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(
                msg.author,
                f"Your balance isn't high enough to complete this tip. You have **{available_balance} {Env.currency_symbol()}**, but this tip would cost you **{amount_needed} {Env.currency_symbol()}**"
            )
            return

        # Make the transactions in the database
        tx_list = []
        task_list = []
        for u in users_to_tip:
            tx = await Transaction.create_transaction_internal(
                sending_user=user,
                amount=individual_send_amount,
                receiving_user=u)
            if tx is not None:
                tx_list.append(tx)
                if not await user.is_muted_by(u.id):
                    task_list.append(
                        Messages.send_basic_dm(
                            member=u,
                            message=
                            f"You were tipped **{individual_send_amount} {Env.currency_symbol()}** by {msg.author.name.replace('`', '')}.\nUse `{config.Config.instance().command_prefix}mute {msg.author.id}` to disable notifications for this user.",
                            skip_dnd=True))
        if len(tx_list) < 1:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(
                msg.author,
                f"No users you mentioned are eligible to receive tips.")
            return
        # Send DMs
        asyncio.ensure_future(Utils.run_task_list(task_list))
        # Add reactions
        await Messages.add_tip_reaction(msg, amount_needed)
        # Queue the actual sends
        for tx in tx_list:
            await TransactionQueue.instance().put(tx)
        # 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(amount_needed)
Exemplo n.º 29
0
    async def tipfavorites_cmd(self, ctx: Context):
        if ctx.error:
            await Messages.add_x_reaction(ctx.message)
            return

        msg = ctx.message
        user = ctx.user
        send_amount = ctx.send_amount

        # Check anti-spam
        if not ctx.god and await RedisDB.instance().exists(f"tipfavoritesspam{msg.author.id}"):
            await Messages.add_timer_reaction(msg)
            await Messages.send_basic_dm(msg.author, "You can only tipfavorites once every 5 minutes")
            return

        # Get their favorites
        favorites = await Favorite.filter(user=user).prefetch_related('favorited_user').all()
        if len(favorites) < 1:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(msg.author, "You don't have any favorites, add some first.")
            return

        individual_send_amount = NumberUtil.truncate_digits(send_amount / len(favorites), max_digits=Env.precision_digits())
        if individual_send_amount < Constants.TIP_MINIMUM:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(msg.author, f"Tip amount too small, each user needs to receive at least {Constants.TIP_MINIMUM}. With your tip they'd only be getting {individual_send_amount}")
            return

        # See how much they need to make this tip.
        amount_needed = individual_send_amount * len(favorites)
        available_balance = Env.raw_to_amount(await user.get_available_balance())
        if amount_needed > available_balance:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(msg.author, f"Your balance isn't high enough to complete this tip. You have **{available_balance} {Env.currency_symbol()}**, but this tip would cost you **{amount_needed} {Env.currency_symbol()}**")
            return

        # Make the transactions in the database
        tx_list = []
        task_list = []

        for u in favorites:
            tx = await Transaction.create_transaction_internal_dbuser(
                sending_user=user,
                amount=individual_send_amount,
                receiving_user=u.favorited_user
            )
            if tx is not None:
                tx_list.append(tx)
                if not await user.is_muted_by(u.favorited_user.id):
                    task_list.append(
                        Messages.send_basic_dm(
                            member=self.bot.get_user(u.favorited_user.id),
                            message=f"You were tipped **{individual_send_amount} {Env.currency_symbol()}** by {msg.author.name.replace('`', '')}.\nUse `{config.Config.instance().command_prefix}mute {msg.author.id}` to disable notifications for this user."
                        )
                    )
        if len(tx_list) < 1:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(msg.author, f"No users you mentioned are eligible to receive tips.")
            return
        # Send DMs
        asyncio.ensure_future(Utils.run_task_list(task_list))
        # Add reactions
        await Messages.add_tip_reaction(msg, amount_needed)
        # Queue the actual sends
        for tx in tx_list:
            await TransactionQueue.instance().put(tx)
        # anti spam
        await RedisDB.instance().set(f"tipfavoritesspam{msg.author.id}", "as", expires=300)
        # 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(amount_needed)
Exemplo n.º 30
0
    triggers = ["ticketstatus", "ts"],
    overview = "Check entry status",
    details = "See if you are entered into the current giveaway, if there is one"
)
GIVEAWAYSTATS_INFO = CommandInfo(
    triggers = ["giveawaystats", "gs"],
    overview = "View stats related to the currently active giveaway",
    details = "View time left, number of entries, and other information about the currently active giveaway"
)
WINNERS_INFO = CommandInfo(
    triggers = ["winners"],
    overview = "View recent giveaway winners",
    details = "View the 10 most recent giveaways winners as well as the amount they've won."
)
TIPGIVEAWAY_INFO = CommandInfo(
    triggers = ["donate", "do"] if Env.banano() else ["ntipgiveaway", "ntg"],
    overview = "Donate to giveaway",
    details = "Donate to the currently active giveaway to increase the pot, or donate to towards starting a giveaway automatically." +
                f"\nExample: `{config.Config.instance().command_prefix}{'donate' if Env.banano() else 'ntipgiveaway'} 1` - Donate 1 {Env.currency_symbol()} to the current or next giveaway"
                f"\nWhen **{config.Config.instance().get_giveaway_auto_minimum()} {Env.currency_symbol()}** is donated, a giveaway will automatically begin."
)

class GiveawayCog(commands.Cog):
    def __init__(self, bot: Bot):
        self.bot = bot
        self.logger = logging.getLogger()
        self.giveaway_ids = []

    @commands.Cog.listener()
    async def on_ready(self):
        # Get active giveaways