Source code for updaters.twitter

"""Module containing class for sending X/Twitter messages."""

import logging
import re

import tweepy

from trackers.config import twitter_config
from trackers.models import Mention
from updaters.base import BaseUpdater

logger = logging.getLogger(__name__)


[docs] class TwitterUpdater(BaseUpdater): """Main class for retrieving and adding X/Twitter messages. :var TwitterUpdater.client: authenticated Twitter client :type TwitterUpdater.client: :class:`tweepy.Client` """ def __init__(self, *args, **kwargs): """Initialize Twitter/X updater. :var config: configuration dictionary for X API :type config: dict """ super().__init__(*args, **kwargs) config = twitter_config() self.client = tweepy.Client( bearer_token=config["bearer_token"], consumer_key=config["consumer_key"], consumer_secret=config["consumer_secret"], access_token=config["access_token"], access_token_secret=config["access_token_secret"], )
[docs] def add_reaction_to_message(self, url, reaction_name): """Add reaction to message. :param url: URL of the message to react to :type url: str :param reaction_name: name of the reaction to add (e.g. "duplicate") :type reaction_name: str :return: X/Twiiter doesn't implement emoji rections so we just return True :rtype: Boolean """ return True
[docs] def add_reply_to_message(self, url, text): """Add reply `text` to the tweet defined by `url`. :param url: URL of the message to reply to :type url: str :param text: text to reply with :type text: str :var tweet_id_match: tweet ID matching object :type tweet_id_match: :class:`re.Match` :var tweet_id: unique tweet identifier :type tweet_id: str :var response: Tweepy client's response object :type response: :class:`requests.Response` :return: True for success, False otherwise :rtype: Boolean """ try: tweet_id_match = re.search(r"(?:twitter\.com|x\.com)/\w+/status/(\d+)", url) if not tweet_id_match: logger.error(f"Invalid tweet URL format: {url}") return False tweet_id = tweet_id_match.group(1) response = self.client.create_tweet( text=text, in_reply_to_tweet_id=tweet_id ) if not response.data: logger.error( f"Failed to add reply: {response.status_code} - {response.text}" ) return False except tweepy.TweepyException as e: logger.error(f"Error replying to tweet: {e}") return False except ValueError as e: logger.error(f"Error: {e}") return False except Exception as e: logger.error(f"Unexpected error: {e}") return False logger.info(f"Reply added successfully to tweet_id {tweet_id}!") return True
[docs] def message_from_url(self, url): """Retrieve message content from provided Twitter `url`. :param url: twitter URL to get message from :type url: str :return: dictionary with message data :rtype: dict """ return Mention.objects.message_from_url(url)