Git Product home page Git Product logo

slackers's People

Contributors

5n7-sk avatar gh-maggio avatar jamestiotio avatar kylecrawshaw avatar tenzer avatar uhavin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

slackers's Issues

[Question] How to test events or commands?

Hello, could you please help me to create some examples how to test handlers?

eg.
This is a handler using a save_text function, I know I cam simply test save_text and other parts.


from fastapi import FastAPI
from slackers.hooks import commands
from slackers.models import SlackCommand
from slackers.server import router

app = FastAPI(debug=True)
app.include_router(router)
app.include_router(router, prefix="/slack")


async def save_text(text: str):
    with open("command_log.txt", "a+") as outf:
        print(text, file=outf)


@commands.on("cmd")
async def handle_command(payload):
    cmd = SlackCommand(**payload).text
    # .. more complex parsing of cmd in between
    await save_text(cmd)

This is my test:

from fastapi.testclient import TestClient


def test_shout_command( client: TestClient):
    command_data = {
        "token": "1234erfghjuiop",
        "command": "/cmd",
        "response_url": "https://hooks.slack.com/commands/ABCDEFG/1289665550278/6RQ7UIUXhMzAFXwApOPiMB1o",
        "trigger_id": "1296602173907.524169929122.0fa6d05ec3b37886607b56011efcbc9d",
        "user_id": "UFE4ZTC8J",
        "user_name": "1user1",
        "team_id": "TFE4ZTB3L",
        "channel_id": "D0184EACFNK",
        "text": "to <@UJ03ECZLG|1user1_1> <@UJ03E1ESU|1user1_2> for less",
    }
    head = {
        "x-slack-signature": "mySecret",
        "x-slack-request-timestamp": "1531420618"
    }
    resp = client.post("/slack/commands", data=command_data, headers=head)
    print(resp)
    assert resp.ok

But response I receive is 403.

Could you please recommend how can I test handle_command with pytest?

custom response

Thanks for this great library! I was able to process actions but I'm having issues with custom responses. I have my code that looks as follows:

@actions.on("block_actions:approve_workflow")
def handle_workflow_unpause_action(payload):
    log.info("Action started.")
    log.debug(payload)
    print("action: ", payload)


@responder("block_actions:approve_workflow")
def respond_to_workflow_unpause_action(payload):
    log.info("Response.")
    log.debug(payload)
    print("response: ", payload)
    user = payload["user"]["username"]
    return JSONResponse(
        {
            "replace_original": "true",
            "text": f"Workflow approved by @{user}",
        }
    )

The issue is I'm expecting the original block_action to be replaced by the message Workflow approved by {user}. But this doesn't seem to be happening. Is there something I'm missing? Thanks for your help and providing this library!

Receiving actions by callback_id

I try to receive actions by callback_id but it only reaches /actions witch does nothing but to return a 200 Ok response :

@commands.on("newreq3")
def handle_command(payload):
    print("Command received")
    print(payload)
    slack_web_client.api_call(
        api_method="dialog.open",
        json= { 
        "trigger_id": payload["trigger_id"],
        "dialog" : {
            "title": "Nouvelle permission",
            "submit_label": "Envoyer",
            "callback_id": "newreqc",
[...]

@actions.on("block_actions:newreqc")
def handle_action(payload):
    print("Action started.")
    print(payload)

@actions.on("block_actions")
def handle_action2(payload):
    slack_web_client.chat_postMessage(
            channel='C01EWV6DL9K',
            text="Asking for permission ?")
    print(payload)

So the dialog box well open when launching command, the submit button sends a callback on /actions only where I think it should send a callback on /actions/newreqc.
Here with your stack I can't see what's inside the callback since I didn't succeed in catching it.

INFO:     3.90.1.212:0 - "POST /commands HTTP/1.0" 200 OK
Command received
{'token': '---------', 'command': '/newreq3', 'response_url': 'https://hooks.slack.com/commands/T01F673DS04/1524134228434/ib7GScKFh45MqXa9juInAZnW', 'trigger_id': '1517403300102.1516241468004.4231bd72bc1b564c08851f55f5947949', 'user_id': 'U01ETA7UDJA', 'user_name': 'gs', 'team_id': 'T01F673DS04', 'channel_id': 'C01EWV6DL9K', 'text': ''}
INFO:     54.198.138.148:0 - "POST /actions HTTP/1.0" 200 OK

Input validation for view_submission actions

Hi @uhavin, I would like to bring up this issue regarding input validation for view_submission actions.

Modals provide the ability for developers to add input blocks to which users can respond to. If a developer were to attempt to validate the inputs sent through a view_submission action, they can follow the instructions detailed here.

Basically, the developer could respond with this payload:

{
  "response_action": "errors",
  "errors": {
    "<block_id>": "Some text here"
  }
}

However, using this library, currently it is not possible for us to respond with a custom object as such (if it is possible, do enlighten me on how to execute it). From what I know, this part of the code will be sent first before any custom code is executed:

return Response()

Due to its empty body, Slack's server will assume that everything went fine and the modal will just be closed without any further follow-up. Thus, any object that is going to be returned after that will be ignored.

Is it possible to somehow modify the aforementioned part of the code to enable developers to provide input validation? Perhaps some kind of option to write a custom object that overrides the default one? Another idea would be to include a try clause. Thank you!

Actions seems to not be called

Hi! Really appreciate your setup for using Fastapi for the slack bot. I was setting this up myself, replicating all the code.

When clicking a button in a slack message, the following events are printed (printing the variable event) in the emit function in hooks.py:

block_actions
block_actions:et6Dk
block_actions:button

Hence, the action response from slack is received. I'm testing this locally using ngrok. I have created this action listener:

# Listening for the action type.
@actions.on("block_actions")
def handle_action(payload):
    log.info("Action started.")
    log.debug(payload)

However, when clicking the button, I don't see anything being registered to the log. Shouldn't the function above be called? Is the log not being registered somehow since it is within an async process?

I'm running the starting the fast API server local by calling:

python3 -m uvicorn PROJECTNAME.main:app --reload

Add tests for valid signatures

In the http tests, hmac is mocked and the signatures all evaulate to valid. There are tests with unmocked hmac, but they only touch the failing scenarios.

There really should be a test that verifies signatures.

[BUG] Interactive messages don't have 'action_id'

Hello there!

I've found an issue. When I have an action type being interactive_message, the data I receive doesn't contain an action_id, only name, type and selected_options, therefore I receive this error:

INFO:     34.207.162.7:0 - "POST /slack/actions HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/home/marcelo/anaconda3/envs/coinprice/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 389, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/home/marcelo/anaconda3/envs/coinprice/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/home/marcelo/anaconda3/envs/coinprice/lib/python3.8/site-packages/fastapi/applications.py", line 149, in __call__
    await super().__call__(scope, receive, send)
  File "/home/marcelo/anaconda3/envs/coinprice/lib/python3.8/site-packages/starlette/applications.py", line 102, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/marcelo/anaconda3/envs/coinprice/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/home/marcelo/anaconda3/envs/coinprice/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/home/marcelo/anaconda3/envs/coinprice/lib/python3.8/site-packages/starlette/exceptions.py", line 82, in __call__
    raise exc from None
  File "/home/marcelo/anaconda3/envs/coinprice/lib/python3.8/site-packages/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/home/marcelo/anaconda3/envs/coinprice/lib/python3.8/site-packages/starlette/routing.py", line 550, in __call__
    await route.handle(scope, receive, send)
  File "/home/marcelo/anaconda3/envs/coinprice/lib/python3.8/site-packages/starlette/routing.py", line 227, in handle
    await self.app(scope, receive, send)
  File "/home/marcelo/anaconda3/envs/coinprice/lib/python3.8/site-packages/starlette/routing.py", line 41, in app
    response = await func(request)
  File "/home/marcelo/anaconda3/envs/coinprice/lib/python3.8/site-packages/fastapi/routing.py", line 196, in app
    raw_response = await run_endpoint_function(
  File "/home/marcelo/anaconda3/envs/coinprice/lib/python3.8/site-packages/fastapi/routing.py", line 148, in run_endpoint_function
    return await dependant.call(**values)
  File "/home/marcelo/Development/fastapi/slackers/src/slackers/server.py", line 47, in post_actions
    triggered_events = [
  File "/home/marcelo/Development/fastapi/slackers/src/slackers/server.py", line 48, in <listcomp>
    f"{action.type}:{triggered_action['action_id']}"

Can someone help me?

Changing the conditionals on the server.actions solves the problem:

@router.post(
    "/actions",
    status_code=HTTP_200_OK,
    dependencies=[Depends(verify_signature), Depends(check_timeout)],
)
async def post_actions(request: Request) -> Response:
    form = await request.form()
    form_data = json.loads(form["payload"])
    # have the convenience of pydantic validation
    action = SlackAction(**form_data)
    _events = [action.type]
    if action.callback_id:
        _events.append(f"{action.type}:{action.callback_id}")
    elif action.actions: 
        triggered_events = [
            f"{action.type}:{triggered_action['action_id']}"
            for triggered_action in action.actions
        ]
        _events.extend(triggered_events)

@uhavin are you still maintaining this package?

500 Error when Trying to Verify Slack App

We're trying to incorporate this library into an FAQ API we've built on top of FastAPI.

When we try to enable the endpoint via Slack, the response is an HTTP error.

Here is the output from uvicorn:

INFO:     44.195.79.65:52938 - "POST /slack/events HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/uvicorn/protocols/http/h11_impl.py", line 396, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/usr/local/lib/python3.8/dist-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/fastapi/applications.py", line 199, in __call__
    await super().__call__(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/applications.py", line 112, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/usr/local/lib/python3.8/dist-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/middleware/cors.py", line 78, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/exceptions.py", line 82, in __call__
    raise exc from None
  File "/usr/local/lib/python3.8/dist-packages/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/usr/local/lib/python3.8/dist-packages/starlette/routing.py", line 580, in __call__
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/routing.py", line 241, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/routing.py", line 52, in app
    response = await func(request)
  File "/usr/local/lib/python3.8/dist-packages/fastapi/routing.py", line 191, in app
    solved_result = await solve_dependencies(
  File "/usr/local/lib/python3.8/dist-packages/fastapi/dependencies/utils.py", line 548, in solve_dependencies
    solved = await call(**sub_values)
  File "/usr/local/lib/python3.8/dist-packages/slackers/verification.py", line 29, in verify_signature
    os.environ.get("SLACK_SIGNING_SECRET").encode(), to_verify, sha256
AttributeError: 'NoneType' object has no attribute 'encode'

The routes appear on the FastAPI /docs page (screenshot attached)
FastAPI_routes

For now, we've just included the very basics of the code implementation, which we understand should automatically handle the Slack Events requests. We've also confirmed that the env variables are set on the OS (Ubuntu).


from fastapi import FastAPI, HTTPException
import uvicorn

# SlackBot Imports
import os
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
from slackers.server import router
from slackers.hooks import actions
from slackers.hooks import responder
from slackers.hooks import events
from dotenv import load_dotenv

# Slack variables
load_dotenv()
signing_secret = os.environ.get("SLACK_SIGNING_SECRET")
slack_token = os.environ.get("SLACK_BOT_TOKEN")

app = FastAPI()
app.include_router(router, prefix='/slack')

Any help is greatly appreciated and please let me know if you need more info.

Message builder .blocks formatter

The format of the message that the MessageBuilder.dict returns, is good for the Slack API. The python slackclient however, implements chat_postMessage accepting a blocks parameter. It would be convenient to have a helper method on the message object, so we can use it like so

slack_web_client.chat_postMessage(channel="ABC", blocks=message.slack_blocks())

422 Unprocessable Entity for app_home_opened

I am unable to process app_home_opened events, getting 422 errors. I am able to process 'message.im' events fine. Is there an issue with the incoming payloads on these two being different?

https://api.slack.com/events/message.im
{ "token": "one-long-verification-token", "team_id": "T061EG9R6", "api_app_id": "A0PNCHHK2", "event": { "type": "message", "channel": "D024BE91L", "user": "U2147483697", "text": "Hello hello can you hear me?", "ts": "1355517523.000005", "event_ts": "1355517523.000005", "channel_type": "im" }, "type": "event_callback", "authed_teams": [ "T061EG9R6" ], "event_id": "Ev0PV52K21", "event_time": 1355517523 }

https://api.slack.com/events/app_home_opened
{ "type": "app_home_opened", "user": "U061F7AUR", "channel": "D0LAN2Q65", "event_ts": "1515449522000016", "tab": "home", "view": { "id": "VPASKP233", "team_id": "T21312902", "type": "home", "blocks": [ ... ], "private_metadata": "", "callback_id": "", "state":{ ... }, "hash":"1231232323.12321312", "clear_on_close": false, "notify_on_close": false, "root_view_id": "VPASKP233", "app_id": "A21SDS90", "external_id": "", "app_installed_team_id": "T21312902", "bot_id": "BSDKSAO2" } }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.