Пример #1
0
    async def handle(message: SlackMessage, bot: SlackAdapter):
        if not bot.addressed_by(message):
            return

        if not bot.understands(
                message, with_pattern=re.compile('card prices?[?]?$', re.I)):
            return

        res = bot.reply(message,
                        text='Here are the current prices for creating cards:',
                        blocks=price_blocks())
        if not res['ok']:
            print(res)
Пример #2
0
    async def handle(message: SlackMessage, bot: SlackAdapter, db: DB):
        pattern = re.compile('balance', re.IGNORECASE)

        if not bot.addressed_by(message) or not bot.understands(
                message, with_pattern=pattern):
            return

        with db.session_scope() as session:
            user = User.get_by_slack_id(session, message.user)
            balance = get_kkred_balance(user, session)

        pluralized_kkreds = 'kkred' if balance == 1 else 'kkreds'
        return bot.reply(message,
                         f'your balance is {balance} {pluralized_kkreds}')
Пример #3
0
    async def handle(message: SlackMessage, bot: SlackAdapter, db: DB):

        pattern = "|".join(
            [".*(?:gibbe|give) money.*", ".*pay me.*", ".*:watermelon:.*"])

        trigger = re.compile(pattern, re.IGNORECASE)

        message_ts = arrow.get(message.ts, 'X')

        if not is_payable(message_ts) or not bot.understands(
                message, with_pattern=trigger):
            return

        user_id = message.user

        with db.session_scope() as session:
            user = User.get_by_slack_id(session, user_id)

            if not user:
                return

            latest_mine = session \
                .query(KKredsTransaction) \
                .filter(KKredsTransaction.to_user_id == user.id) \
                .filter(KKredsTransaction.is_mined) \
                .order_by(KKredsTransaction.created_at.desc()) \
                .first()

            if latest_mine and latest_mine.created_at:
                message_ts_stripped = strip_date(message_ts)
                latest_mine_time_stripped = strip_date(latest_mine.created_at)
                if latest_mine_time_stripped >= message_ts_stripped:
                    return

            amount = 1

            if should_2020_04_mega_pay(message_ts):
                amount = randint(1, 10_000)

            kaori_user = User.get_by_slack_id(session, bot.id)
            mined_kkred = KKredsTransaction(from_user=kaori_user,
                                            to_user=user,
                                            amount=amount,
                                            is_mined=True,
                                            created_at=message_ts.datetime)

            session.add(mined_kkred)

        pluralized = '1 kkred' if amount == 1 else f'{amount} kkreds'
        bot.reply(message, f'successfully mined {pluralized}')
Пример #4
0
    async def handle(message: SlackMessage, bot: SlackAdapter):
        if not bot.addressed_by(message):
            return

        asking_help = re.compile(r'(?:cards|gacha) ?(?:help|-h|--help)?', re.I)
        question = re.compile(r'(?:what|why|how|explain).+(?:cards?|gacha)',
                              re.I)

        if not (bot.understands(message, with_pattern=asking_help)
                or bot.understands(message, with_pattern=question)):
            return

        bot.reply(message,
                  blocks=help_blocks(),
                  create_thread=True,
                  reply_broadcast=True)
Пример #5
0
async def test_help_command(fake_slack_msg_factory,
                            fake_slack_adapter: SlackAdapter):

    bogus_message = fake_slack_msg_factory(text=f'@kaori asldfkjslkdjf')
    fake_slack_adapter.reply = MagicMock(return_value={'ok': True})

    await CardHelpCommand.handle(bogus_message, bot=fake_slack_adapter)
    await CardPriceCommand.handle(bogus_message, bot=fake_slack_adapter)

    fake_slack_adapter.reply.assert_not_called()

    await CardHelpCommand.handle(fake_slack_msg_factory(
        text='@kaori cards help',
        bot=fake_slack_adapter,
    ),
                                 bot=fake_slack_adapter)

    ehhhh = ujson.dumps(fake_slack_adapter.reply.call_args[1])
    assert 'kaori show cards' in ehhhh
    assert 'kaori battle NAME vs. NAME' in ehhhh

    await CardPriceCommand.handle(fake_slack_msg_factory(
        text='@kaori card prices',
        bot=fake_slack_adapter,
    ),
                                  bot=fake_slack_adapter)

    ehhhh = ujson.dumps(fake_slack_adapter.reply.call_args[1])
    assert 'price breakdown' in ehhhh
Пример #6
0
    async def handle(message: SlackMessage, bot: SlackAdapter):
        if not bot.addressed_by(message):
            return

        match = bot.understands(message, with_pattern=re.compile('clap (.*)'))

        if not match:
            return

        try:
            args = clap_parser.parse_args(match.group(1).split())
        except SlackArgumentParserException as err:
            # lol commented out for max sass
            # send(str(err))
            return bot.respond(message, random_insult())

        if args.help:
            return bot.respond(message, clap_parser.get_help())

        if not args.message:
            return bot.respond(message, random_insult())

        new_message = ' {} '.format(args.separator).join(args.message)

        if args.at:
            new_message = '{} {}'.format(args.at, new_message)

        bot.respond(message, new_message)
Пример #7
0
def refresh_card_preview(card: Card, bot: SlackAdapter):
    res = bot.edit(
        SlackMessage({
            'event': {
                'ts': card.draft_message_ts,
                'channel': card.creation_thread_channel
            }
        }), **render_card(card, preview_header=True))
    if not res['ok']:
        print(res)
Пример #8
0
    async def handle(message: SlackMessage, bot: SlackAdapter):
        if message.user == bot.id:
            return

        text = message.text.strip()
        tokens = text.split()
        if not text:
            return

        if bot.mentioned.directly(tokens[0]) and tokens[0].endswith('?'):
            bot.reply(message, random.choice(im_here_response))

        if interrogative_greeting.search(text) and bot.mentioned.anywhere(
                text):
            return bot.reply(message,
                             random.choice(interrogative_greeting_response))

        if greeting.search(text) and bot.mentioned.anywhere(text):
            return bot.reply(message, random.choice(greeting_response))
Пример #9
0
    async def handle(message: SlackMessage, bot: SlackAdapter, db: DB):
        if not bot.addressed_by(message):
            return

        match = bot.understands(message,
                                with_pattern=re.compile('refresh users'))

        if not match:
            return

        res = bot.client.api_call('users.list')

        if not res['ok']:
            raise RuntimeError('call to users.list failed')

        slack_members = [
            member for member in res['members'] if not member['deleted']
        ]

        with db.session_scope() as session:

            def maybe_create(slack_id):
                return User.maybe_create_user_from_slack_id(
                    slack_id, bot.client, session)

            kaori_members = [
                maybe_create(member['id']) for member in slack_members
            ]

            for member in kaori_members:
                el = [x for x in slack_members if x['id'] == member.slack_id]
                if not el:
                    continue

                el = el[0]

                if member.name != el['name']:
                    member.name = el['name']

        bot.reply(message, 'Refreshed users. :^)')
Пример #10
0
    async def handle(message: SlackMessage, bot: SlackAdapter):
        if not bot.addressed_by(message):
            return

        if bot.understands(message, with_pattern=re.compile('ping$', re.I)):
            bot.reply(message, 'pong')
            return

        if bot.understands(message, with_pattern=re.compile('bing$', re.I)):
            bot.reply(message, 'BONG', create_thread=True)
            return
Пример #11
0
    async def handle(message: SlackMessage, bot: SlackAdapter, db: DB,
                     battler: CardBattler):
        if not bot.addressed_by(message):
            return

        requested_battle = _user_requesting_battle(message=message, bot=bot)

        if not requested_battle:
            return

        attacker_search = requested_battle[1]
        defender_search = requested_battle[2]

        with db.session_scope() as session:

            attacker = Card.search_for_one(session, attacker_search)

            if not attacker:
                bot.reply(message,
                          f'no card named "{attacker_search}"',
                          create_thread=True)
                return

            defender = Card.search_for_one(session, defender_search)

            if not defender:
                bot.reply(message,
                          f'no card named "{defender_search}"',
                          create_thread=True)
                return

            battle_url = battler.get_battle_url(attacker.engine,
                                                defender.engine)
            bot.reply(message,
                      create_thread=True,
                      blocks=battle_blocks(attacker=attacker,
                                           defender=defender,
                                           battle_url=battle_url))
Пример #12
0
    async def handle(message: SlackMessage, bot: SlackAdapter, db: DB):
        if not bot.addressed_by(message):
            return

        show_card_pattern = '|'.join([
            '(?:get|show|find) card'
        ])
        show_card_pattern = f'(?:{show_card_pattern})'
        pattern = re.compile(f'{show_card_pattern} (.+)', re.I)
        search = bot.understands(message, with_pattern=pattern)

        if not search:
            return

        with db.session_scope() as session:

            card = Card.search_for_one(session, search[1])

            if not card:
                bot.reply(message, 'no card with that name', create_thread=True)
                return

            bot.reply(message, **render_card(card))
Пример #13
0
    async def handle(message: SlackMessage, bot: SlackAdapter, db: DB):
        if not bot.addressed_by(message):
            return

        show_card_pattern = '|'.join([
            'cards? (?:stats|statistics)'
        ])
        if not bot.understands(message, with_pattern=re.compile(show_card_pattern, re.I)):
            return

        with db.session_scope() as session:

            game_cards = get_game_cards(session)

            if not game_cards:
                bot.reply(message, "No cards", create_thread=True)
                return

        bot.reply(message,
                  blocks=card_stats_blocks(card_total=len(game_cards)),
                  create_thread=True)

        report = generate_report_charts(game_cards)

        bot.client.api_call(
            'files.upload',
            channels=message.channel,
            thread_ts=message.ts,
            filename='rarity.png',
            file=report['rarity'])

        bot.client.api_call(
            'files.upload',
            channels=message.channel,
            thread_ts=message.ts,
            filename='natures.png',
            file=report['natures'])
Пример #14
0
    async def handle(message: SlackMessage, bot: SlackAdapter, db: DB):
        if not bot.addressed_by(message):
            return

        show_card_pattern = '|'.join([
            '(?:get|gimme|show|find|list) ?(?:my|muh)? cardo?s'
        ])
        if not bot.understands(message, with_pattern=re.compile(show_card_pattern, re.I)):
            return

        with db.session_scope() as session:

            cards = session.query(Card) \
                .join(User) \
                .filter(User.slack_id == message.user) \
                .filter(Card.published == True) \
                .limit(10) \
                .all()

            if not cards:
                bot.reply(message, "You don't have any cards yet.", create_thread=True)
                return

            bot.reply(message, create_thread=True, blocks=card_index_blocks(cards))
Пример #15
0
                    environment=config.KIZUNA_ENV)

rabbitmq_broker = RabbitmqBroker(url=config.RABBITMQ_URL)
dramatiq.set_broker(rabbitmq_broker)

if not config.SLACK_API_TOKEN:
    raise ValueError('You are missing a slack token! Please set the SLACK_API_TOKEN environment variable in your '
                     '.env file or in the system environment')

sc = SlackClient(config.SLACK_API_TOKEN)
db_engine = create_engine(config.DATABASE_URL)
make_session = sessionmaker(bind=db_engine, autoflush=False)

k = Kaori()

k.adapters['slack'] = SlackAdapter(slack_client=sc)

k.skills |= {
    DB(make_session=make_session),
}

if hasattr(config, 'USE_GCLOUD_STORAGE') and config.USE_GCLOUD_STORAGE:
    creds = service_account.Credentials.from_service_account_info(config.GCLOUD_SERVICE_ACCOUNT_INFO)
    bucket = storage.Client(project=creds.project_id, credentials=creds).bucket(config.IMAGES_BUCKET_GCLOUD)

    k.skills.add(GCloudStorageUploader(bucket=bucket,
                                       base_path=config.IMAGES_BUCKET_PATH))

elif config.KIZUNA_ENV == 'development':
    k.skills.add(LocalFileUploader())
else:
Пример #16
0
def test_card_creation_state_happy(make_fake_user, grant_kkreds):
    config = test_config
    db_engine = create_engine(config.DATABASE_URL)
    make_session = sessionmaker(bind=db_engine, autoflush=False)

    k = Kaori()

    initial_ts = str(time())
    slack = Mock(spec=SlackClient)
    slack.api_call = MagicMock(return_value={
        'ok': True,
        'ts': initial_ts,
    })
    adapter = SlackAdapter(slack_client=slack)
    adapter._cached_bot_id = token_hex(2)
    k.adapters['slack'] = adapter

    db = DB(make_session=make_session)

    k.skills |= {
        db,
        CardBattler(player_url_base='https://battle.kaori.io/')
    }

    k.skills.add(LocalFileUploader())

    k.plugins |= {
        gacha_plugin,
    }

    u: User = make_fake_user()
    slack_id = u.slack_id
    user_id = u.id

    grant_kkreds(u, 1e10)

    def handle(msg):
        k.handle('slack', msg)

    channel = "CXXXXXX"

    def user_message(**kwargs):
        ts = time()
        return {
            "team_id": "TXXXX",
            "event": {
                "type": "message",
                "user": slack_id,
                "ts": ts,
                "channel": channel,
                "event_ts": ts,
                "channel_type": "channel",
                **kwargs,
            },
            "type": "event_callback",
        }

    with db.session_scope() as session:
        handle(user_message(text='@kaori create card', ts=initial_ts, event_ts=initial_ts))

        card = session.query(Card) \
            .join(User) \
            .filter(Card.creation_thread_ts == initial_ts) \
            .first()

        assert card.owner == user_id
        assert card.creation_cursor == 'set_name'

        name = f'Matt Morgan {token_hex(2)}'

        handle(user_message(text=f'@kaori {name}', thread_ts=initial_ts))

        session.refresh(card)
        assert card.name == name
        assert card.creation_cursor == 'set_image'

        # TODO skipping over image uploading lmao
        card.creation_cursor = 'set_description'
        session.commit()

        handle(user_message(text=f'@kaori ubu uwu', thread_ts=initial_ts))

        session.refresh(card)
        assert card.description == 'ubu uwu'

        handle(user_message(text=f'@kaori stupid feral', thread_ts=initial_ts))

        session.refresh(card)
        assert card.primary_nature == 'stupid'
        assert card.secondary_nature == 'feral'

        handle(user_message(text=f'@kaori S', thread_ts=initial_ts))

        session.refresh(card)
        assert card.rarity_string() == 'S'

        assert card.published is False

        handle(user_message(text=f'@kaori yes', thread_ts=initial_ts))

        session.refresh(card)
        assert card.published is True
        assert card.creation_cursor == 'done'
Пример #17
0
    async def handle(message: SlackMessage, bot: SlackAdapter, db: DB,
                     file_uploader: FileUploader):
        if not bot.addressed_by(message):
            return

        # start a conversation
        if not bot.understands(message,
                               with_pattern=re.compile(
                                   r'create\s+card|card\s+create$', re.I)):
            return

        if message.is_thread_reply:
            return

        try:
            with db.session_scope() as session:
                user = User.get_by_slack_id(session, message.user)

                if not user:
                    raise UserNotFound('cannot find user')

                # allow creation to recover from errors
                card = resume_card(session,
                                   thread_ts=message.thread_ts,
                                   user=message.user)

                if not card:
                    card = initialize_card(message, user)

                    session.add(card)
                    session.commit()

                draft_message = bot.reply(message,
                                          **render_card(card=card,
                                                        preview_header=True),
                                          create_thread=True,
                                          reply_broadcast=True)

                bot.reply(
                    message,
                    blocks=instructions_blocks(bot_name=bot.mention_string),
                    create_thread=True)

                if not draft_message.get('ok'):
                    print(draft_message)
                    return

                card.draft_message_ts = draft_message.get('ts')
                session.merge(card)

        except UserNotFound as e:
            bot.reply(
                message,
                "Something is wrong...cannot find your user. Try 'kaori refresh users'"
            )
            return

        # fake thread
        # todo: this is kinda dumb
        message = copy.deepcopy(message)
        message.is_thread = True
        message.thread_ts = message.ts

        await UpdateCardCommand.handle(message=message,
                                       bot=bot,
                                       db=db,
                                       file_uploader=file_uploader)
Пример #18
0
    async def handle(message: SlackMessage, bot: SlackAdapter, db: DB,
                     file_uploader: FileUploader):
        if not bot.addressed_by(message) or not message.is_thread:
            return

        try:
            session: Session
            with db.session_scope() as session:
                card = resume_card(session,
                                   thread_ts=message.thread_ts,
                                   user=message.user)

                # this thread is not related to a card creation, ignore
                if not card:
                    return

                # lol god this is quickly getting away from me
                # just let me finish this and I'll refactor later
                user_input = ''

                catch_all_pattern = re.compile(r'(.*)', re.IGNORECASE)
                matches = bot.understands(message,
                                          with_pattern=catch_all_pattern)

                if not matches:
                    return

                user_input = matches[1].strip()

                if user_input == 'refresh preview':
                    refresh_card_preview(card, bot)
                    return

                if card.creation_cursor == 'set_image':
                    if message.files:
                        img = Image.from_slack_message(message=message,
                                                       session=session,
                                                       slack_adapter=bot,
                                                       uploader=file_uploader)
                        card.image = img
                        card.creation_cursor = 'query_description'
                        bot.react(message, 'thumbsup')
                    else:
                        bot.reply(message, 'upload an image to slack')
                        return

                card, replies = next_card_creation_step(
                    card=card,
                    user_input=user_input,
                    session=session,
                    kaori_user=User.get_by_slack_id(session, bot.id))

                refresh_card_preview(card, bot)

                session.merge(card)
                session.commit()

                for reply in replies:
                    if reply == ':+1:':
                        bot.react(message, 'thumbsup')
                    else:
                        if isinstance(reply, dict):
                            bot.reply(message, create_thread=True, **reply)
                        else:
                            bot.reply(message, reply, create_thread=True)

        except InvalidCardName as e:
            bot.reply(message, str(e))
        except IntegrityError as e:
            bot.reply(
                message,
                f"Something is wrong with that input...try again or ask Austin to fix it. Code {e.code}"
            )
            print(e)
Пример #19
0
    async def handle(message: SlackMessage, bot: SlackAdapter, db: DB):

        pattern = re.compile(r'(?:pay|tip|give|send)\s+(\S*)\s+(\S*)',
                             re.IGNORECASE)

        if not bot.addressed_by(message):
            return

        matches = bot.understands(message, with_pattern=pattern)

        if not matches:
            return

        message_ts = arrow.get(message.ts, 'X')

        sending_user_id = message.user

        with db.session_scope() as session:
            sending_user = User.get_by_slack_id(session, sending_user_id)

            if not sending_user:
                return

            receiving_user_raw = matches[1]

            if not is_user_mention(receiving_user_raw):
                return bot.reply(
                    message,
                    'User has to be an `@` mention. Like it has to be a real blue `@` mention.'
                )

            receiving_user = User.get_by_slack_id(
                session, extract_user_id_from_mention(receiving_user_raw))

            if not receiving_user:
                return bot.reply(message, 'Could not find that user')

            if sending_user.id == receiving_user.id:
                return bot.reply(message, 'You can’t send money to yourself.')

            amount_raw = matches[2]

            try:
                amount = Decimal(amount_raw)
            except InvalidOperation:
                return bot.reply(
                    message,
                    'That amount is invalid. Try a decimal or integer value')

            if amount <= 0:
                return bot.reply(message, 'Amount has to be non-zero')

            if amount > get_kkred_balance(sending_user, session):
                return bot.reply(message, 'You don’t have enough kkreds')

            transaction = KKredsTransaction(from_user=sending_user,
                                            to_user=receiving_user,
                                            amount=amount,
                                            created_at=message_ts.datetime)

            session.add(transaction)

            bot.reply(message,
                      f'successfully sent {amount} to {receiving_user.name}')
Пример #20
0
def _user_requesting_battle(message: SlackMessage,
                            bot: SlackAdapter) -> Optional[Match]:
    pattern = re.compile(r'battle\s+(.+)\s+vs?\.?\s+(.+)', re.I)
    return bot.understands(message, with_pattern=pattern)