Fixed bug where duplicate threads would not get responded
This commit is contained in:
		| @@ -3,8 +3,8 @@ import configuration | ||||
| import requests | ||||
| import os | ||||
| import time | ||||
| import asyncio | ||||
| import sys | ||||
| from threading import Thread | ||||
| from slack_sdk.errors import SlackApiError | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
| @@ -18,26 +18,29 @@ def init(client) -> None: | ||||
|     global slack_client | ||||
|     slack_client = client | ||||
|  | ||||
|     # config["archive_id"] = channel_id | ||||
|     global LATEST_RECORDED_REACTION | ||||
|     try: | ||||
|         LATEST_RECORDED_REACTION = models.Reaction.select(models.Reaction.id).order_by("id")[-1] | ||||
|     except IndexError: #query is actually empty, we have never fetched any messages until now | ||||
|         LATEST_RECORDED_REACTION = 0     | ||||
|     # fetch all te messages we could have possibly missed | ||||
|      | ||||
|     # fetch all te messages we could have possibly missed | ||||
|     logger.info("Querying missed messages, threads and reactions. This can take some time.") | ||||
|     fetch_missed_channel_messages() | ||||
|     if "nofetch" in sys.argv: | ||||
|         logger.info("Omitted update of reactions and thread messages because of argument 'nofetch'.") | ||||
|     fetch_missed_channel_messages() # not threaded | ||||
|     t = Thread(target = fetch_missed_channel_reactions) # threaded, runs in background (usually takes a long time) | ||||
|     t.start() | ||||
|  | ||||
|     if "reducedfetch" in sys.argv: | ||||
|         logger.warning("Only fetching empty threads for bot messages because of argument 'reducedfetch'") | ||||
|         fetch_missed_thread_messages(reduced=True) | ||||
|     else:    # perform these two asyncronously | ||||
|         async def run_async(): | ||||
|             await asyncio.gather(fetch_missed_channel_reactions(), fetch_missed_thread_messages()) | ||||
|         asyncio.run(run_async()) | ||||
|         fetch_missed_thread_messages() | ||||
|      | ||||
|  | ||||
|  | ||||
| def get_past_messages(): | ||||
| def get_unhandled_messages(): | ||||
|     """Gets all messages that have not yet been handled, be it by mistake or by downtime | ||||
|     As the message handler mkaes no distinction between channel messages and thread messages, | ||||
|     As the message handler makes no distinction between channel messages and thread messages, | ||||
|     we don't have to worry about them here. | ||||
|     """ | ||||
|  | ||||
| @@ -51,10 +54,11 @@ def get_past_messages(): | ||||
|     logger.info(f"Set {len(threaded_objects)} thread-messages as not yet handled.") | ||||
|  | ||||
|  | ||||
|     channel_objects = [t.initiator_message for t in models.Thread.select() if t.message_count == 1 and not t.is_fully_processed] | ||||
|     channel_objects = [t.initiator_message for t in models.Thread.select() if (t.message_count == 1 and not t.is_fully_processed)] | ||||
|     logger.info(f"Set {len(channel_objects)} channel-messages as not yet handled.") | ||||
|      | ||||
|     reaction_objects = list(models.Reaction.select().where(models.Reaction.id > LATEST_RECORDED_REACTION)) | ||||
|     logger.info(f"Set {len(reaction_objects)} reactions as not yet handled.") | ||||
|     # the ones newer than the last before the fetch | ||||
|      | ||||
|     all_messages = channel_objects + threaded_objects | ||||
| @@ -108,11 +112,17 @@ def fetch_missed_channel_messages(): | ||||
|     logger.info(f"Fetched {new_fetches} new channel messages.") | ||||
|  | ||||
|  | ||||
| async def fetch_missed_thread_messages(): | ||||
| def fetch_missed_thread_messages(reduced=False): | ||||
|     """After having gotten all base-threads, we need to fetch all their replies"""         | ||||
|     # I don't know of a better way: we need to fetch this for each and every thread (except if it is marked as permanently solved) | ||||
|     logger.info("Starting async fetch of thread messages...") | ||||
|     threads = [t for t in models.Thread.select() if not t.is_fully_processed] | ||||
|     logger.info("Starting fetch of thread messages...") | ||||
|     if reduced: | ||||
|         threads = [t for t in models.Thread.select() if (t.message_count == 1 and not t.is_fully_processed)] | ||||
|         # this only fetches completely empty threads, which might be because the bot-message was not yet saved to the db. | ||||
|         # once we got all the bot-messages the remaining empty threads will be the ones we need to process. | ||||
|     else: | ||||
|         threads = [t for t in models.Thread.select() if not t.is_fully_processed] | ||||
|     logger.info(f"Fetching history for {len(threads)} empty threads") | ||||
|     new_messages = [] | ||||
|     for i,t in enumerate(threads): | ||||
|         try: | ||||
| @@ -123,7 +133,7 @@ async def fetch_missed_thread_messages(): | ||||
|             )["messages"] | ||||
|         except SlackApiError: | ||||
|             logger.error("Hit rate limit while querying threaded messages, retrying in {}s ({}/{} queries elapsed)".format(config["api_wait_time"], i, len(threads))) | ||||
|             await asyncio.sleep(int(config["api_wait_time"])) | ||||
|             time.sleep(int(config["api_wait_time"])) | ||||
|             messages = slack_client.conversations_replies( | ||||
|                 channel = config["archive_id"], | ||||
|                 ts = t.slack_ts, | ||||
| @@ -140,8 +150,8 @@ async def fetch_missed_thread_messages(): | ||||
|     logger.info("Fetched {} new threaded messages.".format(len(new_messages))) | ||||
|  | ||||
|  | ||||
| async def fetch_missed_channel_reactions(): | ||||
|     logger.info("Starting async fetch of channel reactions...") | ||||
| def fetch_missed_channel_reactions(): | ||||
|     logger.info("Starting background fetch of channel reactions...") | ||||
|     threads = [t for t in models.Thread.select() if not t.is_fully_processed] | ||||
|     for i,t in enumerate(threads): | ||||
|         try: | ||||
| @@ -152,7 +162,7 @@ async def fetch_missed_channel_reactions(): | ||||
|             reactions = query["message"].get("reactions", []) # default = [] | ||||
|         except SlackApiError: # probably a rate_limit: | ||||
|             logger.error("Hit rate limit while querying reactions. retrying in {}s ({}/{} queries elapsed)".format(config["api_wait_time"], i, len(threads))) | ||||
|             await asyncio.sleep(int(config["api_wait_time"])) | ||||
|             time.sleep(int(config["api_wait_time"])) | ||||
|             reactions = query["message"].get("reactions", []) | ||||
|  | ||||
|         for r in reactions: | ||||
|   | ||||
| @@ -1,9 +1,7 @@ | ||||
| from threading import Thread | ||||
| from slack_bolt import App | ||||
| from slack_bolt.adapter.socket_mode import SocketModeHandler | ||||
|  | ||||
| import logging | ||||
| from rich.rule import Rule | ||||
| import configuration | ||||
|  | ||||
| from . import message_helpers | ||||
| @@ -18,12 +16,11 @@ class BotApp(App): | ||||
|     def __init__(self, callback, *args, **kwargs): | ||||
|  | ||||
|         super().__init__(*args, **kwargs) | ||||
|         # models = models | ||||
|         self.callback = callback | ||||
|  | ||||
|     def start(self): | ||||
|         message_helpers.init(self.client) | ||||
|         missed_messages, missed_reactions = message_helpers.get_past_messages() | ||||
|         missed_messages, missed_reactions = message_helpers.get_unhandled_messages() | ||||
|  | ||||
|         [self.handle_incoming_message(m) for m in missed_messages] | ||||
|         [self.handle_incoming_reaction(r) for r in missed_reactions] | ||||
| @@ -122,10 +119,8 @@ class BotApp(App): | ||||
|                 ) | ||||
|  | ||||
|  | ||||
|     def respond_channel_message(self, article, say=message_helpers.say_substitute): | ||||
|         # extra={"markup": True} | ||||
|         # self.logger.info(Rule(url[:min(len(url), 30)])) | ||||
|         thread = article.slack_thread.execute()[0] | ||||
|     def respond_channel_message(self, thread, say=message_helpers.say_substitute): | ||||
|         article = thread.article | ||||
|         answers = article.slack_info | ||||
|         for a in answers: | ||||
|             if a["file_path"]: | ||||
| @@ -149,7 +144,6 @@ class BotApp(App): | ||||
|                     thread_ts=thread.slack_ts | ||||
|                 ) | ||||
|                 status = True | ||||
|         # self.logger.info(Rule(f"Fully handled (success={status})")) | ||||
|          | ||||
|  | ||||
|     def startup_status(self): | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Remy Moll
					Remy Moll