I'm trying to learn how to crate Slack Apps using the Slack Bolt Framewok, so I'm studying this tutorial (https://api.slack.com/start/building/bolt-python). I followed all the steps but my bot is not listening to the events.
The python class:
import os
from slack_bolt import App
app = App(
token = 'xoxb-xxxxxxxx-2463004875172-0pnMlWzr30pFg2vFGtt52wzx',
signing_secret = 'xxxxxxx3eba2b1fc11bf6076'
)
# Start your app
if __name__ == "__main__":
app.start(port=int(os.environ.get("PORT", 3000)))
#app.event("message")
def handle_message_events(body, logger):
logger.info(body)
The response I get when I write a message to the bot:
response
The response in the ngrok:
ngrok response
The bot events subscriptions
subscriptions
Does anyone know what I am forgetting to do?
As per the official documentation, functionality goes before you start the app.
https://github.com/slackapi/bolt-python#creating-an-app
app = App()
# Add functionality here
if __name__ == "__main__":
app.start(3000) # POST http://localhost:3000/slack/events
Can you move following code accordingly, and check?
#app.event("message")
def handle_message_events(body, logger):
logger.info(body)
Related
as the title states, I'm writing a Slack Bot in Python and using NGROK to host it locally. I'm not super experienced with decorators, and I can get the bot posting messages in slack, however I can't seem to handle two events at once. For example, I want to handle a message and have the message keep repeating in slack until a thumbs up reaction is added to that message. The issue is I cannot figure out how to handle an event while another event is still running, please see the following code:
rom slack import WebClient
import os
import time
from pathlib import Path
from dotenv import load_dotenv
from flask import Flask
from slackeventsapi import SlackEventAdapter
env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)
app = Flask(__name__)
slack_event_adapter = SlackEventAdapter(
os.environ['SIGNING_SECRET'],'/slack/events',app)
client = WebClient(token=os.environ['SLACK_TOKEN'])
BOT_ID = client.api_call("auth.test")['user_id']
state = {}
#slack_event_adapter.on('message')
def handle_message(event_data):
message = event_data.get('event', {})
channel_id = message.get('channel')
user_id = message.get('user')
text = message.get('text')
messageid = message.get('ts')
state[messageid] = {"channel_id": channel_id, "user_id": user_id, "text": text}
if BOT_ID != user_id:
if text[0:12] == ":red_circle:":
time.sleep(5)
client.chat_postMessage(channel=channel_id, text=text)
if text[0:21] == ":large_yellow_circle:":
client.chat_postMessage(channel=channel_id, text="it's a yellow question!")
if text[0:14] == ":white_circle:":
client.chat_postMessage(channel=channel_id, text="it's a white question!")
#slack_event_adapter.on('reaction_added')
def reaction_added(event_data):
reaction = event_data.get('event',{})
emoji = reaction.get('reaction')
emoji_id = reaction.get('item',{}).get('ts')
emoji_channel_id = reaction.get('item',{}).get('channel')
client.chat_postMessage(channel=emoji_channel_id, text=emoji)
for message_id, message_data in state.items():
channel_id = message_data["channel_id"]
text = message_data["text"]
client.chat_postMessage(channel=channel_id, text=text)
print(message_id,message_data)
if __name__ == "__main__":
app.run(debug=True)
I can handle individual events, but I cannot handle them while another is running. Please help! :)
Flask is a synchronous web framework.
When it's running a view handler, it uses up a web worker thread. If you does something like time.sleep(...), that worker thread will still be occupied and unavailable to handle other requests until the sleep finishes.
There are a couple options you can do here.
You can use Bolt for Python, which is a Python Slack library that natively support asynchronous even processing. Instead of time.sleep(), you can do await asyncio.sleep(...), which returns the thread to the async loop, and allow the worker thread to process other events.
If you already have an existing slack application and don't want to rewrite your entire codebase to Bolt, then you'll need to handle the event processing yourself. You can do this by doing your work in an ThreadLoopExecutor, or by building your own async event Queue mechanism, or use Celery. Or if your slack bot has very low volume, you can probably just add more web workers, and hope for the best that you don't run out of workers.
I'm writing a web hook server to receive updates when my git repository is pushed.
Upon receiving the POST request from GitHub, I execute several commands like git pull, mvn install which take a very long time.
But the web hook request sent by GitHub timeouts after 10 seconds.
My code:
import logging
import os
from fastapi import FastAPI
app = FastAPI()
logger = logging.getLogger("uvicorn")
def exec_cmd(command):
out = os.system(command)
logger.info(str(out))
#app.post('/')
def func():
logger.info("WebHook received")
exec_cmd("git pull")
exec_cmd("mvn clean install")
exec_cmd("killall java")
return {}
if __name__ == "__main__":
import uvicorn
exec_cmd("git pull")
uvicorn.run("main:app", debug=False, reload=False, host="0.0.0.0")
Therefore I want to run the long running tasks in the background, and respond to GitHub's request as soon as possible.
How can I do this?
(If I make the exec_cmd() function async, when the request returns, the exec_cmd() function doesn't run till completion. )
They are called BackgroundTasks in FastAPI and can be used by adding a BackgroundTasks type to your view function signature.
The example given in the documentation can be further adapter to your needs:
from fastapi import BackgroundTasks, FastAPI
app = FastAPI()
def process_repository(email: str, message=""):
exec_cmd("git pull")
exec_cmd("mvn clean install")
exec_cmd("killall java")
#app.post("/")
async def update_repository(background_tasks: BackgroundTasks):
background_tasks.add_task(process_repository)
return {"message": "Repository update has begun"}
Since you don't check the results this should work for your use case.
I'm using following slack events python adapter:
https://github.com/slackapi/python-slack-events-api
And the code from the tutorial doesn't work:
from flask import Flask
from slackeventsapi import SlackEventAdapter
# This `app` represents your existing Flask app
app = Flask(__name__)
# An example of one of your Flask app's routes
#app.route("/")
def hello():
return "Hello there!"
# Bind the Events API route to your existing Flask app by passing the server
# instance as the last param, or with `server=app`.
slack_events_adapter = SlackEventAdapter(SLACK_SIGNING_SECRET, "/slack/events", app)
# Create an event listener for "reaction_added" events and print the emoji name
#slack_events_adapter.on("reaction_added")
def reaction_added(event_data):
emoji = event_data["event"]["reaction"]
print(emoji)
# Start the server on port 3000
if __name__ == "__main__":
app.run(port=3000)
It doesn't react on any emojies. It looks like putting emoji is not an event...
Resolved.
From api.slack.com -> bot page -> Event Subscriptions -> Subscrive to Bot Events -> add "reaction_add"
I was trying to make a slack bot using slackeventsapi running on ngrok for now.
It can send messages properly but slack events adaptor doesn't seem to be working properly. It gives the code 200 every time a message is sent but the payload doesn't come. I tried printing it but the printing it shows nothing.
There was another post asking a similar question whose solution in the end was to make a new app on slack API but it doesn't seem to fix my issue. I have made another app but the issue persists.
I was following a tutorial so I have tried to match his code exactly but it doesn't seem to work even then. In case it will be helpful - https://www.youtube.com/watch?v=6gHvqXrfjuo&list=PLzMcBGfZo4-kqyzTzJWCV6lyK-ZMYECDc&index=2.
The slack API scopes
Slack API Subscriptions
import slack
import os
from pathlib import Path
from dotenv import load_dotenv
from flask import Flask
from slackeventsapi import SlackEventAdapter
env_path = Path('.')/'.env'
load_dotenv(dotenv_path=env_path)
client = slack.WebClient(token=os.environ['TEST2_SLACK_TOKEN'])
BOT_ID = client.api_call("auth.test")['user_id']
app = Flask(__name__)
slack_event_adaptor = SlackEventAdapter(os.environ['SIGNING_SECRET2'], '/slack/events', app)
client.chat_postMessage(channel=f'#new', text="Hello")
if __name__ == "__main__":
app.run(debug=True)
#slack_event_adaptor.on('message')
def message(payload):
print(payload)
event = payload.get('event',{})
channel_id = event.get('channel')
user_id = event.get('user')
text = event.get('text')
if BOT_ID != user_id:
client.chat_postMessage(channel= channel_id, text = text)
I had similar problem when I used slack_event_adaptor and then I tried slack_bolt and everything works well. Let me share example you may try if you want:
import re
from config import config
from flask import Flask, request
from slack_sdk import WebClient
from slack_bolt import App
from slack_bolt.adapter.flask import SlackRequestHandler
app = Flask(__name__)
slack_token = config.slack_token
client = WebClient(slack_token)
bolt_app = App(token=slack_token, signing_secret=config.signing_secret)
handler = SlackRequestHandler(bolt_app)
#bolt_app.message(re.compile("Hello bot",re.IGNORECASE))
def reply_in_thread(payload: dict):
""" This will reply in thread instead of creating a new thread """
response = client.chat_postMessage(channel=payload.get('channel'),
thread_ts=payload.get('ts'),
text=f"Hello<#{payload['user']}>")
#app.route("/datalake/events", methods=["POST"])
def slack_events():
""" Declaring the route where slack will post a request """
return handler.handle(request)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
When you write "Hello bot" bot will respond you accordingly.
I currently am running two separate python scripts, one for a Flask webapp and the other for a discord.py bot. Upon receiving an incoming request on the flask webapp, I would like to call a function in the discord.py script and return the its output to the view.
Currently both scripts are blocking so I am interested in using a redis messaging queue to pass requests between each other (something like the kombu package).
webapp.py:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def index():
user = get_user(123456789) # get_user function from bot.py
return "Hello, {0}".format(user.name)
if __name__ == "__main__":
app.run()
bot.py:
import discord
client = discord.Client()
async def get_user(user_id):
user = await client.get_user_info(user_id)
return user
if __name__ == "__main__":
client.run("bot token")
I am not sure how to accomplish this task or if Kombu is really the way to go. How would I implement to my flask webapp so that I can call functions within my discord.py bot?
Use import function from other file
from bot import get_user
and make sure, you have both file in same directory.