Ejemplo n.º 1
0
    def command_backup(self, event, args):
        if args.include_channel:
            channel_ids = set()

            for channel_ident in args.include_channel:
                channel_id = _channel_ident_to_channel_id(event.guild, channel_ident)
                if not channel_id:
                    raise CommandError(f'invalid channel to include `{channel_ident}`')
                channel_ids.add(channel_id)
        else:
            channel_ids = set(i.id for i in event.guild.channels.values() if i.type == ChannelType.GUILD_TEXT)

        if args.exclude_channel:
            for channel_ident in args.exclude_channel:
                channel_id = _channel_ident_to_channel_id(event.guild, channel_ident)
                if not channel_id:
                    raise CommandError(f'invalid channel to exclude `{channel_ident}`')
                channel_ids.remove(channel_id)

        backup = self._open_backup(args.name)

        messages_count = 0
        message = event.msg.reply('Preparing to backup...')
        for idx, channel_id in enumerate(channel_ids):
            channel = event.guild.channels[channel_id]
            message.edit(
                f'Backing up channel {channel.mention} ({idx+1}/{len(channel_ids)} | {messages_count} total messages)'
            )
            messages_count += self._backup_channel(backup, channel, args.dl_attachments)

        message.edit(f'Backup complete, a total of {messages_count} messages where preserved')
        backup.close()
Ejemplo n.º 2
0
def parse_duration(raw, source=None, negative=False, safe=False):
    if not raw:
        if safe:
            return None
        raise CommandError('Invalid duration')

    value = 0
    digits = ''

    for char in raw:
        if char.isdigit():
            digits += char
            continue

        if char not in UNITS or not digits:
            if safe:
                return None
            raise CommandError('Invalid duration')

        value += UNITS[char](int(digits))
        digits = ''

    if negative:
        value = value * -1

    return (source or datetime.utcnow()) + timedelta(seconds=value + 1)
Ejemplo n.º 3
0
    def get_user_info(self, target: str, guild: int = None):
        """
        Used to get a Discord user's information from the SQL server.

        Args:
            target: int/str
                The target user's Discord id or their name/alias.

        Return dict format:
            "user_id": int
                The user's Discord id.
            "username": 2 <= string <= 15
                The user's Last.FM username.
            "period": string [
                                'overall',
                                '7day',
                                '1month',
                                '3month',
                                '6month',
                                '12month'
                            ]
                The period which 'Top' commands should use.
            "guild": int
                The guild id used for alias lookup.
        """
        try:
            target = AT_to_id(target)
        except CommandError as e:
            if guild is not None and not isinstance(guild, int):
                data = handle_sql(
                    db_session.query(aliases).filter(
                        aliases.guild_id == guild,
                        aliases.alias.like(target)).first)
                if data:
                    target = data.user_id
                else:
                    raise CommandError("User alias not found.")
            elif isinstance(guild, bool):
                raise CommandError("User aliases aren't enabled in DMs.")
            else:
                raise e
        data = handle_sql(
            db_session.query(users).filter_by(user_id=target, ).first)
        if data is None:
            user = users(user_id=target)
            handle_sql(db_session.add, user)
            handle_sql(db_session.flush)
            data = {"user_id": target, "username": None, "period": periods[0]}
        else:
            data = {
                "user_id": data.user_id,
                "username": data.last_username,
                "period": periods[data.period],
            }
        if data["username"] is None:
            raise CommandError(
                "User should set a last.fm account using ``fm.username``")
        return data
Ejemplo n.º 4
0
def api_loop(command, *args, **kwargs):
    init_time = time()
    while True:
        if time() - init_time > 10:
            raise CommandError("Command timed out.")
        try:
            return command(*args, **kwargs)
        except ConnectionError as e:
            log.info("Didn't catch error reset.")
        except APIException as e:
            if e.code == 50013:
                raise CommandError(
                    "Missing permissions to respond (possibly Embed Links).")
            else:
                log.critical("Api exception: {}: {}".format(e.code, e))
                raise e
Ejemplo n.º 5
0
 def generic_user_data(self,
                       username,
                       title_template="{}",
                       guild=None,
                       **kwargs):
     user_data = self.get_user(username, guild)
     username = user_data["name"]
     if username is None:
         raise CommandError(
             "User should set a last.fm account using ``fm.username``")
     inline = {
         "Playcount":
         user_data["playcount"],
         "Registered":
         strftime(
             "%Y-%m-%d %H:%M",
             gmtime(user_data["registered"]["#text"]),
         ),
     }
     fm_embed = bot.generic_embed_values(
         title=title_template.format(user_data["name"]),
         url=user_data["url"],
         thumbnail=user_data["image"][len(user_data["image"]) - 1]["#text"],
         inlines=inline,
         skip_inlines="N/A",
         **kwargs,
     )
     return fm_embed, user_data["name"]
Ejemplo n.º 6
0
    def command_trader_buy(self, event, entity, amount, trader):
        entity = entity.lower()
        price_per = self._get_price_for_entity(entity)
        price_total = price_per * amount
        if price_total > trader.balance:
            event.msg.reply(
                'Bro... you are gonna need more money for that (${} required) bro...'
                .format(price_total, ))
            return

        current_amount = trader.holdings.get(entity, 0)

        up = Trader.update(
            balance=trader.balance - price_total,
            holdings=Trader.holdings[entity].set(current_amount + amount),
        ).where((Trader.trader_id == trader.trader_id)
                & (Trader.balance == trader.balance)).execute()
        if up != 1:
            self.log.error(
                'Failed to update balance (buy) for {} / {} / {}'.format(
                    entity,
                    amount,
                    trader.trader_id,
                ))
            raise CommandError('Bro... something went wrong bro :(')

        event.msg.reply('Bro... you now own {} (${}) of {} bro...'.format(
            amount,
            price_total,
            entity,
        ))
Ejemplo n.º 7
0
 def pre_check(self, *args):
     """
     Checks to see if api keys are available.
     returns a CommandError if not present
     """
     for key in args:
         if getattr(self, key, None) == None:
             raise CommandError("This function is disabled.")
Ejemplo n.º 8
0
def AT_to_id(id: str):
    if (discord_user_reg.match(str(id)) or discord_nick_reg.match(str(id))
            or discord_id_reg.match(str(id))):
        return int(
            str(id).replace("<", "").replace("@",
                                             "").replace("!",
                                                         "").replace(">", ""))
    else:
        raise CommandError("Invalid @user.")
Ejemplo n.º 9
0
 def remove_player(self, guild_id):
     if guild_id not in self.guilds:
         raise CommandError("I'm not currently playing music here.")
     else:
         self.get_player(guild_id).player.disconnect()
         if self.get_player(guild_id).thread.isAlive():
             self.get_player(guild_id).thread_end = True
             self.get_player(guild_id).thread.join()
         del self.guilds[guild_id]
Ejemplo n.º 10
0
 def get_last_account(self, username: str):
     if self.user_reg.match(username) and 2 <= len(username) <= 15:
         url = "https://ws.audioscrobbler.com/2.0/?method=user.getinfo&user={}&api_key={}&format=json".format(
             username,
             self.last_key,
         )
         user_data = self.get_cached(url, cool_down=1800, item="user")
         return user_data
     else:
         raise CommandError("Invalid username format.")
Ejemplo n.º 11
0
    def _open_backup(self, name):
        if os.path.exists(f'{name}.db'):
            raise CommandError(f'a backup by the name {name} already exists')

        conn = sqlite3.connect(f'{name}.db')
        c = conn.cursor()
        c.execute('CREATE TABLE IF NOT EXISTS messages (id INTEGER, data TEXT)')
        c.execute('CREATE TABLE IF NOT EXISTS attachments (id INTEGER, data BLOB)')
        conn.commit()
        return conn
Ejemplo n.º 12
0
 def on_alias_list_command(self, event, target=None):
     """
     Last.fm Used to get a list of a user's aliases in a guild.
     When no arguments are given, this will return the author's aliases.
     Otherwise, this accepts one argument (a target user's @, ID or alias) and will return a list of the target's alises.
     """
     if event.channel.is_dm:
         api_loop(
             event.channel.send_message,
             "Alias commands are guild specific.",
         )
     else:
         if target is None:
             target = event.author.id
         else:
             try:
                 target = AT_to_id(target)
             except CommandError as e:
                 data = handle_sql(
                     db_session.query(aliases).filter(
                         aliases.guild_id == event.guild.id,
                         aliases.alias.like(target),
                     ).first)
                 if data is None:
                     raise CommandError(
                         "User alias not found in this guild.")
                 else:
                     target = data.user_id
         data = handle_sql(
             db_session.query(aliases).filter_by(
                 user_id=target,
                 guild_id=event.guild.id,
             ).all)
         user = self.client.api.guilds_members_get(event.guild.id, target)
         if data:
             inline = {
                 str(index + 1): alias.alias
                 for index, alias in enumerate(data)
             }
             embed = bot.generic_embed_values(
                 title="{}'s aliases in {}".format(
                     user.name,
                     event.guild.name,
                 ),
                 non_inlines=inline,
             )
             api_loop(
                 event.channel.send_message,
                 embed=embed,
             )
         else:
             api_loop(
                 event.channel.send_message,
                 "User doesn't have any aliases set in this guild.",
             )
Ejemplo n.º 13
0
 def search_devices(self, search, device_filter=None):
     ranks = self.rank_devices(search)
     if device_filter:
         ranks = filter(lambda t: device_filter(t[0]), ranks)
     top = ranks[0]
     top2 = ranks[1]
     if top[1] < 30 or top[1] == top2[1]:
         raise CommandError(':warning: Can\'t find that device! Maybe you mean `{}`, or `{}`?'.format(
             top[0]['name'],
             top2[0]['name']
         ))
     return top[0]
Ejemplo n.º 14
0
 def same_channel_check(self, event):
     try:
         user_state = event.guild.get_member(event.author).get_voice_state()
     except Exception as e:
         log.warning(e)
         user_state = None
     if user_state is None:
         raise CommandError(
             "You need to be in a voice channel to use this command.")
     else:
         bot_state = self.get_player(
             event.guild.id, ).player.client.channel_id
         try:
             same_channel = bot_state == user_state.channel_id
         except CommandError as e:
             raise e
         except Exception as e:
             log.warning(e)
         if not same_channel:
             raise CommandError(
                 "You need to be in the same voice channel to use this command."
             )
Ejemplo n.º 15
0
def parse_message_arg(channel_id, message_raw):
    message_id = None

    if message_raw.isdigit():
        message_id = int(message_raw)
    else:
        res = QUOTE_LINK_RE.findall(message_raw)
        if res:
            channel_id = int(res[0][1])
            message_id = int(res[0][2])

    if not message_id:
        raise CommandError("invalid message id or link provided")

    return channel_id, message_id
Ejemplo n.º 16
0
    def _get_price_for_entity(self, entity):
        if entity in self._price_cache:
            (ts, data) = self._price_cache[entity]
            if ts > time.time() - CACHE_TTL:
                return data
            del self._price_cache[entity]

        value = None
        if entity.startswith('$'):
            r = self._session.get(IEX_URL +
                                  '/stock/{}/price'.format(entity[1:].lower()))
            try:
                r.raise_for_status()
                value = r.json()
            except Exception:
                self.log.exception('Failed to fetch quote: ')
                raise CommandError('failed to fetch quote bro!')
        else:
            raise CommandError(
                'only US stocks prefixed with `$` are supported right now bro...'
            )

        self._price_cache[entity] = (time.time(), value)
        return value
Ejemplo n.º 17
0
 def on_move(self, event):
     # """
     # Voice Move me to another voice channel (this currently clears play queue).
     # Accepts no arguments.
     # """
     raise CommandError("NotImplemented")
     self.pre_check(event)
     if event.guild.id != 152559372126519296:
         return api_loop(
             event.channel.send_message,
             "This command is currently B R O K E and crippingly out of date.",
         )
     self.get_player(event.guild.id).thread.isAlive()
     state = event.guild.get_member(event.author).get_voice_state()
     if not state:
         return api_loop(
             event.channel.send_message,
             "You must be in a voice channel to use that command.",
         )
Ejemplo n.º 18
0
 def get_spotify_auth(self, auth):
     access_time = time()
     r = post(
         "https://accounts.spotify.com/api/token",
         data={"grant_type": "client_credentials"},
         headers={
             "Authorization": "Basic {}".format(auth),
             "User-Agent": self.user_agent,
         },
     )
     if r.status_code != 200:
         log.warning(r.text)
         raise CommandError(
             "Error code {} returned by initial".format(
                 r.status_code,
             )
         )
     self.spotify_auth = r.json()["access_token"]
     self.spotify_auth_expire = r.json()["expires_in"]
     self.spotify_auth_time = access_time
Ejemplo n.º 19
0
    def command_trader_sell(self, event, entity, amount, trader):
        entity = entity.lower()
        if entity not in trader.holdings or trader.holdings[entity] <= 0:
            event.msg.reply(
                'Bro... you don\'t hold any {} bro...'.format(entity))
            return

        current_amount = trader.holdings[entity]
        if current_amount < amount:
            event.msg.reply(
                'Bro... you only have {}x {} to sell bro...'.format(
                    entity, current_amount))
            return

        price = self._get_price_for_entity(entity)

        holdings_upd = None
        if current_amount - amount > 0:
            holdings_upd = Trader.holdings[entity].set(current_amount - amount)
        else:
            holdings_upd = Trader.holdings[entity].remove()

        up = Trader.update(
            balance=trader.balance + (price * amount),
            holdings=holdings_upd,
        ).where((Trader.trader_id == trader.trader_id)
                & (Trader.balance == trader.balance)).execute()
        if up != 1:
            self.log.error(
                'Failed to update balance (sell) for {} / {} / {}'.format(
                    entity,
                    amount,
                    trader.trader_id,
                ))
            raise CommandError('Bro... something went wrong bro :(')

        event.msg.reply('Bro... you sold {} of {} for ${} grats bro...'.format(
            amount,
            entity,
            price * amount,
        ))
Ejemplo n.º 20
0
 def get_player(self, guild_id):
     if guild_id not in self.guilds:
         raise CommandError("I'm not currently playing music here.")
     return self.guilds.get(guild_id)
Ejemplo n.º 21
0
 def pre_check(self, event):
     if event.channel.is_dm:
         raise CommandError(
             "Voice commands aren't allowed in the forbidden lands.")
Ejemplo n.º 22
0
 def add_reactors(self,
                  client,
                  message,
                  reaction,
                  author_id,
                  *args,
                  channel=None,
                  time: int = 30):
     if isinstance(message, Message):
         if (message.id is None or message.channel is None
                 or message.channel.id is None):
             log.info(
                 "Failed to add reactors, either message.id or message.channel or message.channel.id was None."
             )
             return
         message_id = message.id
         channel_id = message.channel.id
     else:
         message_id = int(message)
         if channel_id is not None:
             if isinstance(channel_id, Channel):
                 if channel_id.id is None:
                     log.info(
                         "Failed to add reactors, channel_id.id was None.")
                     return
                 channel_id = channel_id.id
             else:
                 channel_id = int(channel_id)
         else:
             raise Exception(
                 "Unable to add reactor, either unable to work out channel id or missing channel id."
             )
     for reactor in args:
         self.add_argument(
             id=message_id,
             reactor=reactor,
             function=reaction,
             owner_id=author_id,
         )
     for reactor in args:
         try:
             client.client.api.channels_messages_reactions_create(
                 channel_id,
                 message_id,
                 reactor,
             )
         except APIException as e:
             if e.code == 10008:
                 if message_id in self.events:
                     # self.events.pop(message_id, None)
                     del self.events[message_id]
                 break
             elif e.code == 50001:
                 if message_id in self.events:
                     del self.events[message_id]
                 raise CommandError("Missing ``add reactions`` permission.")
             else:
                 raise e
     sleep(time)
     if message_id in self.events:
         del self.events[message_id]
         try:
             client.client.api.channels_messages_reactions_delete_all(
                 channel_id,
                 message_id,
             )
         except APIException as e:
             if e.code == 10008:
                 pass
             elif e.code == 50013:
                 client.client.api.channels_messages_create(
                     channel=channel_id,
                     content=
                     "Missing permission required to clear message reactions ``Manage Messages``.",
                 )
             else:
                 raise e
Ejemplo n.º 23
0
 def on_chart_command(self, event):
     raise CommandError("Not implemented yet, coming soon.")