def format_giveaway_announcement(self,
                                  giveaway: Giveaway,
                                  amount: int = None) -> discord.Embed:
     embed = discord.Embed(
         colour=0xFBDD11 if Env.banano() else discord.Colour.dark_blue())
     embed.set_author(
         name=f"New Giveaway! #{giveaway.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"
     )
     embed.description = f"<@{giveaway.started_by.id if not giveaway.started_by_bot else self.bot.user.id}> has sponsored a giveaway of **{Env.raw_to_amount(int(giveaway.base_amount if amount is None else amount))} {Env.currency_name()}**!\n"
     fee = Env.raw_to_amount(int(giveaway.entry_fee))
     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 = (giveaway.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"
     return embed
Esempio n. 2
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."
        )
Esempio n. 3
0
class Constants(object):
    TIP_MINIMUM = 1.0 if Env.banano() else 1
    TIP_UNIT = 'BAN' if Env.banano() else 'WATERMELONANO'
    WITHDRAW_COOLDOWN = 1 # Seconds
    RAIN_MIN_ACTIVE_COUNT = 4 # Amount of people who have to be active for rain to work
    RAIN_MSG_REQUIREMENT = 5 # Amount of decent messages required to receive rain
    REPRESENTATIVE='ban_1tipbotgges3ss8pso6xf76gsyqnb69uwcxcyhouym67z7ofefy1jz7kepoy' if Env.banano() else 'watermelon1aunch1qxkfrjkmhwhewqx9mucmy3f5n5urnf7xh4dhrs4hecf4fx3mit1sk'
Esempio n. 4
0
    async def ufw_cmd(self, ctx: Context):
        msg = ctx.message

        try:
            addresses = RegexUtil.find_address_matches(msg.content)
        except AddressMissingException:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(msg.author, "No valid addresses in your ufw command")
            return

        address_list = await Account.filter(address__in=addresses).prefetch_related('user').all()
        if len(address_list) < 1:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(msg.author, "No users found with specified addresses.")
            return

        for acct in address_list:
            response = f"Last known name: {acct.user.name}```{acct.user.id}```"
            response += f"```{acct.address}```"
            if Env.banano():
                response += f"https://creeper.banano.cc/explorer/account/{acct.address}\n"
            else:
                response += f"https://nanocrawler.cc/explorer/account/{acct.address}\n"

        embed = discord.Embed(colour=0xFBDD11 if Env.banano() else discord.Colour.green())
        embed.set_author(name="UFW Result", 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.description = response

        await msg.author.send(embed=embed)
Esempio n. 5
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)
Esempio n. 6
0
    async def legacyboard_cmd(self, ctx: Context):
        if ctx.error:
            await Messages.add_x_reaction(ctx.message)
            return

        msg = ctx.message

        if not ctx.god and await RedisDB.instance().exists(
                f"ballerspam{msg.channel.id}"):
            await Messages.add_timer_reaction(msg)
            await Messages.send_error_dm(
                msg.author,
                "Why don't you wait awhile before checking the ballers list again"
            )
            return

        # Get list
        ballers = await Stats.filter(
            server_id=msg.guild.id,
            banned=False).order_by('-legacy_total_tipped_amount'
                                   ).prefetch_related('user').limit(15).all()

        if len(ballers) == 0:
            await msg.channel.send(
                f"<@{msg.author.id}> There are no stats for this server yet, send some tips!"
            )
            return

        response_msg = "```"
        # Get biggest tip to adjust the padding
        biggest_num = 0
        for stats in ballers:
            # TODO change to stats.tip_sum
            length = len(
                f"{NumberUtil.format_float(stats.legacy_total_tipped_amount)} {Env.currency_symbol()}"
            )
            if length > biggest_num:
                biggest_num = length
        for rank, stats in enumerate(ballers, start=1):
            adj_rank = str(rank) if rank >= 10 else f" {rank}"
            user_name = stats.user.name
            amount_str = f"{NumberUtil.format_float(stats.legacy_total_tipped_amount)} {Env.currency_symbol()}"
            response_msg += f"{adj_rank}. {amount_str.ljust(biggest_num)} - by {user_name}\n"
        response_msg += "```"

        embed = discord.Embed(
            colour=0xFBDD11 if Env.banano() else discord.Colour.green())
        embed.set_author(
            name=
            f"Here are the top {len(ballers)} tippers of all time\U0001F44F",
            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.description = response_msg

        await RedisDB.instance().set(f"ballerspam{msg.channel.id}",
                                     "as",
                                     expires=300)
        await msg.channel.send(f"<@{msg.author.id}>", embed=embed)
Esempio n. 7
0
class Constants(object):
    TIP_MINIMUM = 1.0 if Env.banano() else 0.0001
    TIP_UNIT = 'BAN' if Env.banano() else 'Nano'
    WITHDRAW_COOLDOWN = 1  # Seconds
    RAIN_MIN_ACTIVE_COUNT = 5  # Amount of people who have to be active for rain to work
    RAIN_MSG_REQUIREMENT = 5  # Amount of decent messages required to receive rain
    REPRESENTATIVE = 'ban_1tipbotgges3ss8pso6xf76gsyqnb69uwcxcyhouym67z7ofefy1jz7kepoy' if Env.banano(
    ) else 'nano_3o7uzba8b9e1wqu5ziwpruteyrs3scyqr761x7ke6w1xctohxfh5du75qgaj'
Esempio n. 8
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 'WATERMELONANO'}) 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 **WATERMELONANO** 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
Esempio n. 9
0
 def get_giveaway_auto_fee(self) -> float:
     default = 10 if Env.banano() else 1
     if not self.has_yaml():
         return default
     elif 'giveaway' in self.yaml and 'auto_fee' in self.yaml['giveaway']:
         return self.yaml['giveaway']['auto_fee']
     return default
Esempio n. 10
0
 def get_giveaway_minimum(self) -> float:
     default = 1000 if Env.banano() else 5
     if not self.has_yaml():
         return default
     elif 'giveaway' in self.yaml and 'minimum' in self.yaml['giveaway']:
         return self.yaml['giveaway']['minimum']
     return default
Esempio n. 11
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('\:melonT:646094583092084736')
             await msg.add_reaction('\:melonI:645983222068674561')
             await msg.add_reaction('\:melonP:646092262077497379')
             await msg.add_reaction('\:check:646421240600592395')
             if rain:
                 await msg.add_reaction('\:mlnrain:645968989192978442'
                                        )  # Sweat Drops
Esempio n. 12
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))
     active_resource = cors.add(
         self.app.router.add_resource("/active/{server_id}"))
     cors.add(active_resource.add_route("GET", self.get_active))
     self.logger = logging.getLogger()
     self.host = host
     self.port = port
     self.min_amount = 10 if Env.banano() else 0.1
Esempio n. 13
0
 def get_rain_minimum(self) -> int:
     # 1000 BAN default or 1 WATERMELONANO
     default = 1000 if Env.banano() else 25
     if not self.has_yaml():
         return default
     elif 'restrictions' in self.yaml and 'rain_minimum' in self.yaml[
             'restrictions']:
         return self.yaml['restrictions']['rain_minimum']
     return default
Esempio n. 14
0
    async def wfu_cmd(self, ctx: Context):
        msg = ctx.message

        targets = []
        # Get mentioned users
        for m in msg.mentions:
            targets.append(m.id)

        # Get users they are spying on by ID alone
        for sec in msg.content.split():
            try:
                numeric = int(sec.strip())
                user = await self.bot.fetch_user(numeric)
                if user is not None:
                    targets.append(user.id)
            except Exception:
                pass
        targets = set(targets)

        if len(targets) < 1:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(msg.author, "No valid users in your wfu command")
            return

        user_list = await User.filter(id__in=targets).prefetch_related('account').all()
        if len(user_list) < 1:
            await Messages.add_x_reaction(msg)
            await Messages.send_error_dm(msg.author, "None of those users have accounts with me")
            return

        for u in user_list:
            response = f"Last known name: {u.name}```{u.id}```"
            response += f"```{u.account.address}```"
            if Env.banano():
                response += f"https://creeper.banano.cc/explorer/account/{u.account.address}\n"
            else:
                response += f"https://nanocrawler.cc/explorer/account/{u.account.address}\n"

        embed = discord.Embed(colour=0xFBDD11 if Env.banano() else discord.Colour.green())
        embed.set_author(name="WFU Result", 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.description = response

        await msg.author.send(embed=embed)
Esempio n. 15
0
    async def blocks_cmd(self, ctx: Context):
        if ctx.error:
            await Messages.add_x_reaction(ctx.message)
            return

        msg = ctx.message
        is_private = ChannelUtil.is_private(msg.channel)

        if not ctx.god and await RedisDB.instance().exists(
                f"blocksspam{msg.channel.id if not is_private else msg.author.id}"
        ):
            await Messages.add_timer_reaction(msg)
            await Messages.send_error_dm(
                msg.author,
                "Why don't you wait awhile before checking the block count again?"
            )
            return

        count, unchecked = await RPCClient.instance().block_count()
        if count is None or unchecked is None:
            await Messages.send_error_dm(
                msg.author, "I couldn't retrieve the current block count")
            return

        embed = discord.Embed(
            colour=0xFBDD11 if Env.banano() else discord.Colour.dark_blue())
        embed.set_author(
            name=f"Here's how many blocks I have",
            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 = f"```Count: {count:,}\nUnchecked: {unchecked:,}```"

        await RedisDB.instance().set(
            f"blocksspam{msg.channel.id if not is_private else msg.author.id}",
            "as",
            expires=120)
        if is_private:
            await msg.author.send(embed=embed)
        else:
            await msg.channel.send(f"<@{msg.author.id}>", embed=embed)
Esempio n. 16
0
 def find_address_matches(input_text: str) -> List[str]:
     """Find nano/banano addresses in a string"""
     if Env.banano():
         address_regex = '(?:ban)(?:_)(?:1|3)(?:[13456789abcdefghijkmnopqrstuwxyz]{59})'
     else:
         address_regex = '(?:watermelon)(?:1|3)(?:[13456789abcdefghijkmnopqrstuwxyz]{59})'
     matches = re.findall(address_regex, input_text)
     if len(matches) >= 1:
         return matches
     raise AddressMissingException("address_not_found")
Esempio n. 17
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.green())
     embed.set_author(name="Balance", 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.description = "**Available:**\n"
     embed.description += f"```{Env.raw_to_amount(balance_raw - pending_send_db):,} {Env.currency_symbol()}\n"
     pending_receive_str = f" + {Env.raw_to_amount(pending_raw + pending_receive_db):,} {Env.currency_symbol()}"
     pending_send_str = f" - {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
Esempio n. 18
0
 def find_address_match(input_text: str) -> str:
     """Find nano/banano address in a string"""
     if Env.banano():
         address_regex = '(?:ban)(?:_)(?:1|3)(?:[13456789abcdefghijkmnopqrstuwxyz]{59})'
     else:
         address_regex = '(?:watermelon)(?:1|3)(?:[13456789abcdefghijkmnopqrstuwxyz]{59})'
     matches = re.findall(address_regex, input_text)
     if len(matches) == 1:
         return matches[0]
     elif len(matches) > 1:
         raise AddressAmbiguousException("too_many_addresses")
     raise AddressMissingException("address_not_found")
Esempio n. 19
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
 async def notify_user(self, tx: Transaction, hash: str):
     if tx.destination == Env.donation_address():
         return
     bot: Bot = self.bot
     user = bot.get_user(tx.sending_user.id)
     if user is None:
         self.logger.warn(
             f"User with ID {tx.sending_user.id} was not found, so I couldn't notify them of their withdraw"
         )
         return
     if Env.banano():
         await user.send(
             f"Withdraw processed: https://creeper.banano.cc/explorer/block/{hash}"
         )
     else:
         await user.send(
             f"Withdraw processed: https://nanocrawler.cc/explorer/block/{hash}"
         )
Esempio n. 21
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)
Esempio n. 22
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"
Esempio n. 23
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):
Esempio n. 24
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)
Esempio n. 25
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')
Esempio 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)
Esempio n. 27
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):
Esempio n. 28
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
Esempio n. 29
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)
Esempio n. 30
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