Esempio n. 1
0
    def handle(message: SlackMessage, bot: SlackAdapter, db: DB):

        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)

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

            for member in kizuna_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. :^)')
Esempio n. 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 = user.get_kkred_balance(session)

        pluralized_kkreds = 'kkred' if balance == 1 else 'kkreds'
        return bot.reply(message,
                         f'your balance is {balance} {pluralized_kkreds}')
Esempio n. 3
0
    def handle(message: SlackMessage, bot: SlackAdapter, db: DB):
        pattern = re.compile('react(?:ion) add', 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)

            if not user:
                return bot.reply(message, no_user_message, ephemeral=True)

            react_add_image_url = authenticated_path(user, '/react/images/new')

            return bot.reply(message, react_add_image_url, ephemeral=True)
Esempio n. 4
0
    def handle(message: SlackMessage, bot: SlackAdapter, db: DB):
        with db.session_scope() as session:
            user = User.get_by_slack_id(session, message.user)

            if not user:
                return bot.reply(message,
                                 """
                I don't have your user in the db. Prolly run 'kizuna refresh users' and if that still doesn't fix it:
                Austin f****d up somewhere :^(
                """.strip(),
                                 ephemeral=True)

            bot.reply(message,
                      build_url(KIZUNA_WEB_URL, '/login',
                                {'auth': user.get_token()}),
                      ephemeral=True)
Esempio n. 5
0
def test_plugin(capsys):
    k = Kizuna()
    k.adapters['slack'] = SlackAdapter(slack_client=FakeSlackClient)
    k.plugins |= {kizuna.plugins.ping}
    k.handle('slack', slack_ping_message.copy())
    out, err = capsys.readouterr()
    assert out == '<@UUSERRRRR> pong'
Esempio n. 6
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))
Esempio n. 7
0
    async def handle(message: SlackMessage, bot: SlackAdapter):
        if not bot.addressed_by(message):
            return

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

        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)
Esempio n. 8
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)

        if not is_payable(message_ts):
            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

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

            session.add(mined_kkred)

        bot.reply(message, 'successfully mined 1 kkred')
Esempio n. 9
0
    def respond(message: SlackMessage, bot: SlackAdapter, db: DB):
        if not bot.addressed_by(message):
            return

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

        if not matches:
            return

        query = matches.group(1).strip()

        with db.session_scope() as session:

            user = User.get_by_slack_id(session, message.user)

            if not user:
                return bot.reply(message, no_user_message, ephemeral=True)

            react_homepage_url = authenticated_path(user, '/react')
            react_add_image_url = authenticated_path(user, '/react/images/new')

            # kizuna react <tag>
            def add_images_nag():
                add_images = format_url('(Add Images)', react_add_image_url)
                view_images = format_url('(View Images)', react_homepage_url)
                bot.reply(
                    message,
                    f"You can add some images though! {add_images} {view_images}",
                    ephemeral=True)

            possible_tags = [token.strip().lower() for token in query.split()]

            tags = (session.query(ReactionImageTag).options(
                orm.joinedload("images")).filter(
                    ReactionImageTag.name.in_(possible_tags)).all())

            if tags:
                images = list(
                    itertools.chain.from_iterable([tag.images
                                                   for tag in tags]))

                if len(images) > 0:
                    for image in images:
                        image.tags_text = [
                            tag.name for tag in image.tags
                            if tag.name in possible_tags
                        ]
                    images.sort(key=lambda t: len(t.tags_text), reverse=True)
                    best_match_length = len(images[0].tags_text)
                    best_matches = [
                        image for image in images
                        if len(image.tags_text) == best_match_length
                    ]
                    return bot.respond(message, choice(best_matches).url)

                return [
                    bot.respond(
                        message,
                        f"I don't have any images for \"{query}\" :^("),
                    add_images_nag()
                ]

            return [
                bot.respond(message,
                            f"I don't have anything for \"{query}\" :^("),
                add_images_nag()
            ]
Esempio n. 10
0
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)

k = Kizuna()

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

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

k.plugins |= {
    kizuna.plugins.chat, kizuna.plugins.clap, kizuna.plugins.ping,
    kizuna.plugins.kkreds
}


@dramatiq.actor
def slack_worker(payload):
    logging.debug(payload)
    k.handle('slack', payload)
Esempio n. 11
0
 async def handle(message: SlackMessage, bot: SlackAdapter):
     if bot.addressed_by(message) and bot.understands(
             message, with_pattern=re.compile('ping$', re.I)):
         bot.reply(message, 'pong')
Esempio n. 12
0
    async def handle(message: SlackMessage, bot: SlackAdapter, db: DB):

        pattern = re.compile("(?: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)

        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[0]

            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[1]

            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 > sending_user.get_kkred_balance(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}')
Esempio n. 13
0
    def handle(message: SlackMessage, bot: SlackAdapter, db: DB):

        if not bot.addressed_by(message):
            return

        matches = bot.understands(message, with_pattern=re.compile('mentions(?: (.*))?', re.I))

        if not matches:
            return

        user_args = matches.group(1).split() if matches. else []

        try:
            args = parser.parse_args(user_args)
        except SlackArgumentParserException as err:
            return send(str(err))

        user_layout = args.layout
        output_format = 'png' if args.raster else 'pdf'

        def send_message(text):
            return slack_client.api_call("chat.postMessage",
                                         channel=channel,
                                         text=text,
                                         as_user=True)

        if user_layout not in self.available_layouts:
            layout_error_message = ("Oops! --user_layout needs to be one of '{}'. "
                                    "You gave me '{}'").format(', '.join(self.available_layouts), user_layout)
            return send_message(layout_error_message)

        if args.help:
            return send_message(self.help_text)

        with self.db_session_scope() as session:
            edges = session.query(AtGraphEdge).order_by(AtGraphEdge.weight.asc()).all()
            users = session.query(User).order_by(User.name.asc()).all()

            if not edges or len(edges) < 1:
                send_message("Uhh...Could not find any edges in the db. Something is probably wrong.")
                return

            loading_message = slack_client.api_call("chat.postMessage",
                                                    channel=channel,
                                                    text=WAIT_A_SEC + JAP_DOT,
                                                    as_user=True)

            loaded = False

            def continiously_update_loading_message():
                cycle_count = 0
                dot_count = 1
                while not loaded and cycle_count < 20:
                    sleep(1)
                    if loaded:
                        break
                    dot_count = dot_count + 1 if dot_count < 3 else 1
                    new_text = WAIT_A_SEC + (dot_count * JAP_DOT)
                    slack_client.api_call("chat.update",
                                          ts=loading_message['ts'],
                                          channel=channel,
                                          text=new_text,
                                          as_user=True)
                    cycle_count += 1

            thread = Thread(target=continiously_update_loading_message)

            if loading_message['ok']:
                thread.start()

            graph = Digraph(comment='Mentions', format=output_format)
            color_index = 0
            user_color_map = {}
            colors = tableau.get_map('Tableau_20').hex_colors

            for user in users:
                color = colors[color_index]
                color_index = color_index + 1 if color_index < (len(colors) - 1) else 0
                user_color_map[user.name] = color
                graph.node(user.name, color=color)

            max_weight = edges[len(edges) - 1].weight
            min_weight = edges[0].weight

            max_penwidth = 5
            min_penwidth = 0.10

            max_fontsize = 20
            min_fontsize = 7

            def scale_penwidth_by(value):
                return self.linear_scale(max_weight, min_weight, max_penwidth, min_penwidth, value)

            def scale_fontsize_by(value):
                return self.linear_scale(max_weight, min_weight, max_fontsize, min_fontsize, value)

            for edge in edges:
                graph.edge(edge.head_user.name,
                           edge.tail_user.name,
                           penwidth=str(scale_penwidth_by(edge.weight)),
                           label=str(edge.weight),
                           weight=str(edge.weight),
                           fontsize=str(scale_fontsize_by(edge.weight)),
                           fontcolor=str(user_color_map[edge.head_user.name]),
                           color=user_color_map[edge.head_user.name])

            def dot():
                graph.engine = 'dot'

            def neato():
                graph.engine = 'neato'

            def fdp():
                graph.engine = 'fdp'

            def twopi():
                graph.engine = 'twopi'

            def circo():
                graph.engine = 'circo'

            def layout_graph(layout):
                return {
                    "dot": dot,
                    "neato": neato,
                    "fdp": fdp,
                    "twopi": twopi,
                    "circo": circo
                }.get(layout, dot)

            try:
                layout_graph(user_layout)()

                slack_client.api_call('files.upload',
                                      as_user=True,
                                      channels=message['channel'],
                                      filename=f'graph.{output_format}',
                                      file=graph.pipe())
            except CalledProcessError as err:
                send_message('Encountered a problem while rendering the graph :monkas:')
                raise err
            except TypeError as err:
                send_message('Encountered a problem while trying to write the graph to the file system :monkas:')
                raise err
            finally:
                loaded = True
                thread.join()
                slack_client.api_call("chat.delete",
                                      ts=loading_message['ts'],
                                      channel=channel,
                                      as_user=True)