A report is posted every 5 hrs on a Slack channel, from which we need to sort/filter some information and put it into a file.
So, is there any way to read the channel continuously or run some command every 5 minutes or so before that time, and capture the report for future processing?
Yes, that is possible. Here is the basic outline of a solution:
Create a Slack app based on a script (e.g. in Python) that has access to
that channel's history (e.g. has the channels:history permission scope)
Use cron to call your script at the needed time
The script reads the channels history (e.g. with channel.history for public channels), filterers out what it needs
and then stores the report as file.
Another approach would be to continuously read every new message from the channel, parse for a trigger (e.g. a specific user that sends it or the name of the report) and then filter and safe the report when it appears. If you can identify a reliable trigger this would in my experience be the more stable solution, since scheduled reports can be delayed.
For that approach use the Events API of Slack instead of CRON and subscribe to receiving messages (e.g. message event for public channels). Slack will then automatically send each new message to your script as soon as it is posted.
If you are new to creating Slack apps I would advise to study the excellent official documentation and tutorials on the Slack API site to get started.
A Python example to this approach could be found here: https://gist.github.com/demmer/617afb2575c445ba25afc432eb37583b
This script counts the amount of messages per user.
Based on this code I created the following example for you:
# get the correct channel id
for channel in channels['channels']:
if channel['name'] == channel_name:
channel_id = channel['id']
if channel_id == None:
raise Exception("cannot find channel " + channel_name)
# get the history as follows:
history = sc.api_call("channels.history", channel=channel_id)
# get all the messages from the history:
messages = history['messages']
# Or reference them by ID, so in this case get the first message:
ids = messages[0]
Related
I'm building a web-app that runs analysis on Slack activity and I need test data. Do not know where to turn to, doesn't have to be millions of messages, but need data for the following:
user status (online/away/logged off) changes
messages sent (date, user, contents)
reactions (date, type, reaction on what message)
huddles, voice/video chats with metadata
any help is much appreciated
If you're developing an app to be used on the Enterprise Grid plan, you can request a sandbox from Slack directly, though it won't contain any test data off the bat.
More information on that here: https://api.slack.com/enterprise/grid/testing.
That being said, you could populate it with information yourself, using a combination of several different Web API methods. Here are a couple methods that may be of use to you:
chat.postMessage - posts a message in a channel
conversations.create - creates a new channel
reactions.add - adds a reaction onto an item
I am trying to use slack web client API to pull slack messages including threads for a specific date. The conversations.history API only returns the parent message in the case of threaded messages. There is conversations.replies API that returns the message threads, but it requires ts of the parent message to be passed in the request so it will only return conversations related to one thread.
Is there a way to pull all message history including replies for a data range rather than having to combine a call to conversations.history API and then multiple calls to conversations.replies for each message with thread_ts?
This approach of combining both APIs won't work if the reply was posted on the specific date we want to pull, but the root thread message was posted on an older date. The root message won't be returned in conversations.history and hence we won't be able to get that particular message in the thread using conversations.replies.
It's strange that Slack doesn't provide such API to pull all messages including threaded ones.
Unfortunately, there is no way to capture all threads in a workspace with a single API call. The conversations.history is already a very data-heavy method. Most developers calling this method don't need thread information and including that in the reply would be a bit of an overkill. Calling conversations.replies should return all the replies corresponding to that parent message regardless of the date it was posted in unless otherwise specified using the latest or oldest parameters.
I would like to save the conversation references to a blob file or SQL DB so that I can download this file and retrieve the conversation references and then send proactive messages to the users. I know that there is a sample that allows me to save the conversation references in a dictionary, but obviously this dictionary is deleted after a deployment of a new version of the bot and so I can't message the users anymore. So I thought to save this dictionary in a blob file in order to recover it and not to lose the conversation references. But this practice doesn't work.
I do the following to save the dictionary.
a = pickle.dumps(conversation_reference_dict)
blob_client.upload_blob(a, blob_type="BlockBlob", overwrite = True)
But I think this practice saves me a dictionary made like this : [id_client : address of the conversation_reference object] and this is clearly not what I want because after a future deployment this address will no longer mean anything.
Does anyone have any tips for doing this in python?
Thank you very much
UPDATE
After testing, the code is correctly saved in the dictionary. However, the problem arises when I try to execute the following code snippet after an update of the bot's source code.
# Send a message to all conversation members.
# This uses the shared Dictionary that the Bot adds conversation references to.
async def _send_proactive_message():
for conversation_reference in CONVERSATION_REFERENCES_STORED.values():
await ADAPTER.continue_conversation(
conversation_reference,
lambda turn_context: turn_context.send_activity("proactive hello"),
APP_ID,
)
The adapter method fails to continue the conversation with users, as if it no longer found them. Is there a way to update the conversation references that are saved in the blob so that after a release the bot can continue the pending conversations?
UPDATE II
I would like to work in this scenario:
I have my bot quizzing users of a teams channel, the bot is released on azure and I can't go through the internal app section to teams due to lack of permissions.
The bot works with proactive messages and saves the necessary conversation references to a file inside a blob.
I want to introduce a new feature inside the bot so I perform a new release, I would like the bot to be able to continue proactively messaging users, since I save the conversation references to a file that is not touched by this release.
The last point doesn't happen, the proactive messages are no longer sent, is there any way I can continue to send these messages? I'm assuming that in the new bot release new id/url are created and these do not match the old id/url saved on the file and therefore calling the method of sending proactive messages via conversation reference will fail or otherwise not be executed. Does anyone know which fields do not match? Can I possibly send a message to the post-release bot and "modify" the entire dictionary on blob so that these urls/ids match?
I make an example of what i mean: i know that after a release the id1 is modified, so i after the release for example through the test section inside azure i contact the bot, this contact triggers a method that calls the file saved on azure it scrolls it all and replaces the old id1 with the new id1 so the sending of proactive messages can continue safely. is this a possible scenario? Can you help me?
UPDATE III
I seem to have solved my problem by adding this line of code:
AppCredentials.trust_service_url(conversation_reference.service_url)
before:
await ADAPTER.continue_conversation(
conversation_reference,
lambda turn_context: turn_context.send_activity("proactive hello"),
APP_ID,
)
resulting in this final code:
# Send a message to all conversation members.
# This uses the shared Dictionary that the Bot adds conversation references to.
async def _send_proactive_message():
for conversation_reference in CONVERSATION_REFERENCES_STORED.values():
AppCredentials.trust_service_url(conversation_reference.service_url)
await ADAPTER.continue_conversation(
conversation_reference,
lambda turn_context: turn_context.send_activity("proactive hello"),
APP_ID,
)
I'm not sure why this was removed from the docs, but you always need to trust the service URL in order to send proactive messages in Teams, as explained in this answer: Proactive message not working in MS Teams after bot is restarted
EDIT: This is apparently no longer true for all languages, so you may not have to do it in Python for much longer
I'm using the python-telegram-bot library to write a bot in Python that sends URLs into a channel where the bot is administrator.
Now, I would like to have the bot reading, let's say, the last 5 messages (I don't really care about the number as I just need to read the message on the chat) and store them into a list in the code for further elaborations.
I already have my bot working with:
bot = telegram.Bot(token='mytoken')
bot.sendMessage(chat_id='#mychatid', text=entry.link)
But I can't find a bot.getLastMessage or bot.getMessage kind of class into the python-telegram-bot library.
In case there's already no written class that does that, how can I implement it via the Telegram API as I'm a bit of a beginner when it comes to API implementation?
Thanks.
That's not possible in Bots unfortunately.
Here you can find all available methods (that python-telegram-bot invokes behind the scenes) and there's no such method available to fetch messages on demand.
The closest you can get through the api is getChat (which would return the pinned_message in that chat).
What you can do in this case is, store the messages the bot sends as well as the message updates the bot receives (by setting up a handler) in some storage (database) and fetch from there later on.
Have you tried the other type of Telegram API, Telegram [client] API and TDLib?
Using telethon library makes it easy to read channels history (see telethon docs).
For this, we need an api_id and an api_hash.
To get these parameters, we need to log in to our Telegram core
and go to the API development tools area.
There is a simple form that needs to be filled out, after which, we can receive our api_id and api_hash. See Telegram's help documentation about how to get your API credentials.
Here is an example code that gets the last 5 messages of targetChannelId:
from telethon import TelegramClient
API_ID = 123456 # See above for how to get it
API_HASH = '123abc' # See above for how to get it
client = TelegramClient('my-client', API_ID, API_HASH)
async def main():
async for message in client.iter_messages('targetChannelId', limit=5):
print(message.id, message.text)
with client:
client.loop.run_until_complete(main())
The first time you run this code it asks your phone number or bot token. Enter your phone number in the format +9912345... where 99 is your country code and the rest is your phone number.
It then may send a login code to your Telegram app; enter it in the console.
Note: Bots cannot see channel history messages (at least in telethon) but users (phone numbers) can. Bots can only listen for channel updates only if they are one of its administrators.
The client.iter_messages() accepts other parameters like min_id which can be used so we get messages only after a specific message (for example, we can save the last message id that we have processed and next time pass that id as min_id so only messages after that message are returned).
Problem: My use case is I want to receive messages from Google Cloud Pub/Sub - one message at a time using the Python Api. All the current examples mention using Async/callback option for pulling the messages from a Pub/Sub subscription. The problem with that approach is I need to keep the thread alive.
Is it possible to just receive 1 message and close the connection i.e. is there a feature where I can just set a parameter (something like a max_messages) to 1 so that once it receives 1 message the thread terminates?
The documentation here doesn't list anything for Python Synchronous pull which seem to have num_of_messages option for other languages like Java.
See the following example in this link:
from google.cloud import pubsub_v1
client = pubsub_v1.SubscriberClient()
subscription = client.subscription_path('[PROJECT]', '[SUBSCRIPTION]')
max_messages = 1
response = client.pull(subscription, max_messages)
print(response)
I've tried myself and using that you get one message at a time.
If you get some error try updating pubsub library to the last version:
pip install --upgrade google-cloud-pubsub
In docs here you have more info about the pull method used in the code snippet:
The Pull method relies on a request/response model:
The application sends a request for messages. The server replies with
zero or more messages and closes the connection.
As per the official documentation here:
...you can achieve exactly once processing of Pub/Sub message streams,
as PubsubIO de-duplicates messages based on custom message identifiers
or identifiers assigned by Pub/Sub.
So you should be able to use record IDs, i.e. identifiers for you messages, to allow for exactly-once processing across the boundary between Dataflow and other systems. To use record IDs, you invoke idLabel when constructing PubsubIO.Read or PubsubIO.Write transforms, passing a string value of your choice. In java this would be:
public PubsubIO.Read.Bound<T> idLabel(String idLabel)
This returns a transform that's like this one but that reads unique message IDs from the given message attribute.