Skip to content

Tweeter Class

Tweeter

API Object allowing users to tweet articles, summaries and other text to Twitter. It leverages PaLM to summarize articles and then tweets them in chunks of 280 characters. The API keys required for the Twitter API must be provided as environment variables or as arguments to the constructor. The API key required for the Google API can be provided as environment variables or as an argument to the constructor. The Google API key is optional. If not provided, the API will not be able to summarize articles.

ATTRIBUTE DESCRIPTION
__API_KEY

The API key for the Twitter API

TYPE: str

__API_SECRET_KEY

The API secret key for the Twitter API

TYPE: str

__ACCESS_TOKEN

The access token for the Twitter API

TYPE: str

__ACCESS_TOKEN_SECRET

The access token secret for the Twitter API

TYPE: str

__GOOGLE_API_KEY

The API key for the Google API

TYPE: str

__client

The tweepy client object

TYPE: Client

Source code in twitternewsbot/tweeter.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
class Tweeter():

    """
    API Object allowing users to tweet articles, summaries and other text to Twitter. 
    It leverages PaLM to summarize articles and then tweets them in chunks of 280 characters.
    The API keys required for the Twitter API must be provided as environment variables or as arguments to the constructor.
    The API key required for the Google API can be provided as environment variables or as an argument to the constructor. 
    The Google API key is optional. If not provided, the API will not be able to summarize articles.

    Attributes
    ----------
    __API_KEY : str
        The API key for the Twitter API
    __API_SECRET_KEY : str
        The API secret key for the Twitter API
    __ACCESS_TOKEN : str
        The access token for the Twitter API
    __ACCESS_TOKEN_SECRET : str
        The access token secret for the Twitter API
    __GOOGLE_API_KEY : str
        The API key for the Google API
    __client : Client
        The tweepy client object
    """

  #####################################
  # Initialization
  #####################################

    def __init__(self, api_key: str|None = None, api_secret_key: str|None = None, access_token: str|None = None, access_token_secret: str|None = None, google_api_key: str|None = None):
        """Initialize the class with tokens and tweepy client

        Parameters
        ----------
        api_key : str, optional
            The API key for the Twitter API, by default None
        api_secret_key : str, optional
            The API secret key for the Twitter API, by default None
        access_token : str, optional
            The access token for the Twitter API, by default None
        access_token_secret : str, optional
            The access token secret for the Twitter API, by default None
        google_api_key : str, optional
            The API key for the Google API, by default None

        Raises
        ------
        Exception
            If API_KEY is not found in environment variables and not provided as an argument
        Exception
            If API_SECRET_KEY is not found in environment variables and not provided as an argument
        Exception
            If ACCESS_TOKEN is not found in environment variables and not provided as an argument
        Exception
            If ACCESS_TOKEN_SECRET is not found in environment variables and not provided as an argument
        Exception
            Twitter API Authentication Failed. Invalid Twitter API Credentials
        """

        # Load environment variables
        load_dotenv()

        if api_key is None:
            try:
                self.__API_KEY = os.getenv("API_KEY")
            except:
                raise Exception("API_KEY not found in environment variables and not provided as an argument")
        else:
            self.__API_KEY = api_key

        if api_secret_key is None:
            try:
                self.__API_SECRET_KEY = os.getenv("API_SECRET_KEY")
            except:
                raise Exception("API_SECRET_KEY not found in environment variables and not provided as an argument")
        else:
            self.__API_SECRET_KEY = api_secret_key

        if access_token is None:
            try:
                self.__ACCESS_TOKEN = os.getenv("ACCESS_TOKEN")
            except:
                raise Exception("ACCESS_TOKEN not found in environment variables and not provided as an argument")
        else:
            self.__ACCESS_TOKEN = access_token

        if access_token_secret is None:
            try:
                self.__ACCESS_TOKEN_SECRET = os.getenv("ACCESS_TOKEN_SECRET")
            except:
                raise Exception("ACCESS_TOKEN_SECRET not found in environment variables and not provided as an argument")
        else:
            self.__ACCESS_TOKEN_SECRET = access_token_secret

        if google_api_key is None:
            try:
                self.__GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
            except:
                pass
        else:
            self.__GOOGLE_API_KEY = google_api_key

        try:
            self.__client = Client(consumer_key=self.__API_KEY, 
                                   consumer_secret=self.__API_SECRET_KEY, 
                                   access_token=self.__ACCESS_TOKEN, 
                                   access_token_secret=self.__ACCESS_TOKEN_SECRET)
        except:
            raise Exception("Authentication Failed. Invalid Twitter API Credentials")



  #####################################
  # Private Methods
  #####################################

    def __tweet(self, text: str) -> dict:
        """Private: Tweet the given text

        Parameters
        ----------
        text : str
            The text to be tweeted

        Returns
        -------
        dict
            A dictionary containing the total character count, the number of tweets posted, and the id of the parent tweet
        """

        # Create chunks
        total_char_count = len(text)
        text = list(self.__create_chunks(text))


        no_of_chunks = len(text)

        # Seperate parent tweet from children tweets
        parent_tweet_text = text[0]
        leaf_tweets = text[1:]

        # Post parent tweet
        parent_tweet_id = self.__parent_tweet(text=parent_tweet_text)

        # Post children tweets
        for leaf_tweet in leaf_tweets:
            self.__child_tweet(text=leaf_tweet, parent_tweet_id=parent_tweet_id)

        return {"Total Character Count": total_char_count, "No. of Tweets": no_of_chunks, "Parent Tweet ID": parent_tweet_id}

    def __parent_tweet(self, text: str) -> str | None:
        """Post the parent tweet and return the id of the tweet

        Parameters
        ----------
        text : str
            The text to be tweeted

        Returns
        -------
        parent_tweet_id : str
            The id of the tweet
        """
        try:
            parent_tweet_id = self.__client.create_tweet(text=text).data['id']
            return parent_tweet_id
        except Exception as error:
            raise Exception(f"Tweet not posted succesfully: {error}")

    def __child_tweet(self, text: str, parent_tweet_id: str) -> None:
        """Post the child tweet as a reply to the parent tweet

        Parameters
        ----------
        text : str
            The text to be tweeted as a reply to the parent tweet
        parent_tweet_id : str
            The id of the parent tweet

        Returns
        -------
        None
        """
        try:
            self.__client.create_tweet(text=text, in_reply_to_tweet_id=parent_tweet_id)
        except Exception as error:
            raise Exception(f"Tweet not posted succesfully: {error}")


    def __create_chunks(self, text: str) -> list:
        """Create chunks of 280 characters each from the given text while leveraging the yield keyword

        Parameters
        ----------
        text : str
            The text to be chunked

        Returns
        -------
        chunks : list(str)
            A list of 280 char chunks of the given text
        """
        for start in range(0, len(text), 280):
            yield text[start:start + 280]


    def __summarize_article(self, article: dict, prompt: str|None) -> str | None:
        """Summarize the given article using Google PaLM API

        Parameters
        ----------
        article : dict
            A dictionary containing the title and article body of the news article
        prompt : str, optional
            The prompt to be used for the summarization, by default None

        Returns
        -------
        summary : str
            The summary of the article created using GOOGLE PaLM
        """

        # Get the API key
        if self.__GOOGLE_API_KEY is None:
            raise Exception("GOOGLE_API_KEY not found in environment variables")

        # Initialize PaLM
        try:
            palm.configure(api_key=self.__GOOGLE_API_KEY)
        except:
            raise Exception("Authentication Failed. Invalid Google API Credentials")

        # Default Settings
        defaults = {
                        'model': 'models/text-bison-001',
                        'temperature': 0.1,
                        'candidate_count': 1,
                        'top_k': 40,
                        'top_p': 0.95,
                        'max_output_tokens': 1024,
                        'stop_sequences': [],
                        'safety_settings': [{"category":"HARM_CATEGORY_DEROGATORY","threshold":1},{"category":"HARM_CATEGORY_TOXICITY","threshold":1},{"category":"HARM_CATEGORY_VIOLENCE","threshold":2},{"category":"HARM_CATEGORY_SEXUAL","threshold":2},{"category":"HARM_CATEGORY_MEDICAL","threshold":2},{"category":"HARM_CATEGORY_DANGEROUS","threshold":2}],
                    }

        # Create a prompt
        prompt = f"""
        Summarize the following article and condense it into 2 bullet points. Add the title of the article at the top. Do not leave an empty line after the article. Only use information from the article provided below. Structure your response as follows:

        Format for Summary:
        Title
        - Bullet point 1
        - Bullet point 2

        Title - {article['title']}
        Article - {article['article']}

        Summary:
        """ if prompt is None else f"""{prompt}"""

        # Generate the tweet
        try:
            tweet = palm.generate_text(**defaults, prompt=prompt)
            return tweet.result
        except:
            raise Exception("Failed to generate tweet using PaLM")


    def __clean_tweet(self, tweet_text: str) -> str:
        """Clean the tweet by removing unwanted characters as PaLM adds '*' to the tweet occasionally

        Parameters
        ----------
        tweet_text : str
            The text of the tweet to be cleaned

        Returns
        -------
        tweet_text : str
            The cleaned tweet text
        """

        # Remove * from tweet
        tweet_text = tweet_text.replace('*', '')
        return tweet_text

    def __handle_articles_list(self, articles_list: list, title: str|None, prompt: str|None) -> str | None:
        """Handle the list of articles by summarizing them and returning a generated tweet

        Parameters
        ----------
        articles_list : list(dict)
            A list of dictionaries containing the title, source, date and link of the articles
        title : str, optional
            The title of the tweet
        prompt : str, optional
            The prompt to be used for the summarization, by default None

        Returns
        -------
        articles_generated_summary : str
            The generated tweet from the articles
        """

        # Add title of tweet to the beginning of the tweet
        articles_generated_summary = f"{title}:\n\n"

        # Add the summary for each article to the tweet
        for article in articles_list:
            # Call API to get summary
            summary = self.__summarize_article(article, prompt)

            # If summary is None, continue by skipping article
            if summary is None:
                continue

            # Clean the summary
            articles_generated_summary += self.__clean_tweet(summary)

            # Add a new line, formatting
            articles_generated_summary += "\n\n"

        # Return the generated summary
        return articles_generated_summary

    def __generate_with_palm(self, prompt: str|None) -> str | None:
        """Generate a tweet using PaLM

        Parameters
        ----------
        prompt : str, optional
            The prompt to be used for generation, by default None
        to_rewrite : str, optional
            The text to be rewritten, by default None
        Returns
        -------
        generated_summary : str
            The generated tweet
        """

        # Get the API key
        try:
            GOOGLE_API_KEY = os.environ['GOOGLE_API_KEY']
        except:
            raise Exception("GOOGLE_API_KEY not found in environment variables")

        # Initialize PaLM
        try:
            palm.configure(api_key=GOOGLE_API_KEY)
        except:
            raise Exception("Authentication Failed. Invalid Google API Credentials")

        # Default Settings
        defaults = {
                        'model': 'models/text-bison-001',
                        'temperature': 0.1,
                        'candidate_count': 1,
                        'top_k': 40,
                        'top_p': 0.95,
                        'max_output_tokens': 1024,
                        'stop_sequences': [],
                        'safety_settings': [{"category":"HARM_CATEGORY_DEROGATORY","threshold":1},{"category":"HARM_CATEGORY_TOXICITY","threshold":1},{"category":"HARM_CATEGORY_VIOLENCE","threshold":2},{"category":"HARM_CATEGORY_SEXUAL","threshold":2},{"category":"HARM_CATEGORY_MEDICAL","threshold":2},{"category":"HARM_CATEGORY_DANGEROUS","threshold":2}],
                    }

        # Generate the tweet
        try:
            tweet = palm.generate_text(**defaults, prompt=prompt)
            return tweet.result
        except:
            raise Exception("Failed to generate tweet using PaLM")




  #####################################
  # Public Methods - API Methods
  #####################################

    def get_client(self) -> Client:
        """Get the tweepy client object

        Returns
        -------
        client : Client
            The tweepy client object

        Examples
        --------
        >>> from twitternewsbot.tweeter import Tweeter
        >>> tweeter = Tweeter()
        >>> client = tweeter.get_client() # Retrieve your tweety client object
        >>> client.create_tweet(text="Hello World!") # Tweet Hello World using your account's client
        """
        return self.__client

    def tweet(self, title: str|None = None, tweet: str|None = None, articles_list: list|None = None, use_palm: bool = False, prompt: str|None = None) -> dict:
        """Tweet the given articles list

        Parameters
        ----------
        title : str, optional
            The title of the tweet
        tweet : str, optional
            The tweet to be posted
        articles_list : list, optional
            A list of dictionaries containing the title, source, link and text of the articles.
            Obtained from the NewsFinder.get_news_articles() method
        use_palm : bool, optional
            A boolean value indicating whether to use PaLM to generate tweet with prompt
        prompt : str, optional
            The prompt to be used for the summarization with PaLM

        Returns
        -------
        tweet : dict
            A dictionary containing the total character count, the number of tweets posted, and the id of the parent tweet

        Raises
        ------
        TypeError
            If title is not a string
        TypeError
            If tweet is not a string
        TypeError
            If articles_list is not a list of dictionaries
        TypeError
            If use_palm is not a boolean
        ValueError
            If use_palm is True and tweet or articles_list is not None
        ValueError
            If use_palm is True and prompt is None
        ValueError
            If tweet and articles_list are both not None
        TypeError
            If prompt is not a string

        Examples
        --------

        Provide a title and tweet to be posted:

        >>> from twitternewsbot.tweeter import Tweeter
        >>> tweeter = Tweeter()
        >>> tweeter.tweet(title="Hello World", tweet="Hello World!")

        Provide a title and articles list to be posted:

        >>> from twitternewsbot.tweeter import Tweeter
        >>> from twitternewsbot.newsfinder import NewsFinder
        >>> tweeter = Tweeter()
        >>> newsfinder = NewsFinder()
        >>> articles_list = newsfinder.get_news_articles(topic="Bitcoin", num_articles=5, article_text=True) # Article Text must be True if summarizing articles
        >>> tweeter.tweet(title="Bitcoin News", articles_list=articles_list)

        Provide a prompt to be used with PaLM to generate a tweet:

        >>> from twitternewsbot.tweeter import Tweeter
        >>> tweeter = Tweeter()
        >>> tweeter.tweet(title="AI", use_palm=True, prompt="Create a 50 word description of what Artificial Intelligence is.")
        """

        # Check if title is valid
        if title is not None and not isinstance(title, str):
            raise TypeError("title must be a string")

        # Check if tweet is valid
        if tweet is not None and not isinstance(tweet, str):
            raise TypeError("tweet must be a string")

        # Check if articles_list is valid
        if articles_list is not None and (not isinstance(articles_list, list) or not all(isinstance(article, dict) for article in articles_list)):
            raise TypeError("articles_list must be a list of dicts")

        # Check if use_palm is valid
        if use_palm is not None and not isinstance(use_palm, bool):
            raise TypeError("use_palm must be a boolean")

        # Check if use_palm is True, then tweet and articles_list must be None
        if use_palm is True and (tweet is not None or articles_list is not None):
            raise ValueError("tweet and articles_list must be None if use_palm is True")

        # Check if prompt is not provided and use_palm is True
        if prompt is None and use_palm is True:
            raise ValueError("prompt must be provided if use_palm is True")

        # Check if tweet and articles_list are both provided
        if tweet is not None and articles_list is not None:
            raise ValueError("Both tweet and articles_list cannot be provided")

        # Check if prompt is valid
        if prompt is not None and not isinstance(prompt, str):
            raise TypeError("prompt must be a string")

        if articles_list is not None:
            # Handle the articles list
            tweet = self.__handle_articles_list(articles_list, title, prompt)
        elif use_palm:
            # Generate a tweet using PaLM
            tweet = self.__generate_with_palm(prompt)
            if title is not None:
                tweet = f"{title}:\n\n{tweet}"

        return self.__tweet(tweet)

get_client

get_client()

Get the tweepy client object

RETURNS DESCRIPTION
client

The tweepy client object

TYPE: Client

Examples:

>>> from twitternewsbot.tweeter import Tweeter
>>> tweeter = Tweeter()
>>> client = tweeter.get_client() # Retrieve your tweety client object
>>> client.create_tweet(text="Hello World!") # Tweet Hello World using your account's client
Source code in twitternewsbot/tweeter.py
def get_client(self) -> Client:
    """Get the tweepy client object

    Returns
    -------
    client : Client
        The tweepy client object

    Examples
    --------
    >>> from twitternewsbot.tweeter import Tweeter
    >>> tweeter = Tweeter()
    >>> client = tweeter.get_client() # Retrieve your tweety client object
    >>> client.create_tweet(text="Hello World!") # Tweet Hello World using your account's client
    """
    return self.__client

tweet

tweet(title=None, tweet=None, articles_list=None, use_palm=False, prompt=None)

Tweet the given articles list

PARAMETER DESCRIPTION
title

The title of the tweet

TYPE: str DEFAULT: None

tweet

The tweet to be posted

TYPE: str DEFAULT: None

articles_list

A list of dictionaries containing the title, source, link and text of the articles. Obtained from the NewsFinder.get_news_articles() method

TYPE: list DEFAULT: None

use_palm

A boolean value indicating whether to use PaLM to generate tweet with prompt

TYPE: bool DEFAULT: False

prompt

The prompt to be used for the summarization with PaLM

TYPE: str DEFAULT: None

RETURNS DESCRIPTION
tweet

A dictionary containing the total character count, the number of tweets posted, and the id of the parent tweet

TYPE: dict

RAISES DESCRIPTION
TypeError

If title is not a string

TypeError

If tweet is not a string

TypeError

If articles_list is not a list of dictionaries

TypeError

If use_palm is not a boolean

ValueError

If use_palm is True and tweet or articles_list is not None

ValueError

If use_palm is True and prompt is None

ValueError

If tweet and articles_list are both not None

TypeError

If prompt is not a string

Examples:

Provide a title and tweet to be posted:

>>> from twitternewsbot.tweeter import Tweeter
>>> tweeter = Tweeter()
>>> tweeter.tweet(title="Hello World", tweet="Hello World!")

Provide a title and articles list to be posted:

>>> from twitternewsbot.tweeter import Tweeter
>>> from twitternewsbot.newsfinder import NewsFinder
>>> tweeter = Tweeter()
>>> newsfinder = NewsFinder()
>>> articles_list = newsfinder.get_news_articles(topic="Bitcoin", num_articles=5, article_text=True) # Article Text must be True if summarizing articles
>>> tweeter.tweet(title="Bitcoin News", articles_list=articles_list)

Provide a prompt to be used with PaLM to generate a tweet:

>>> from twitternewsbot.tweeter import Tweeter
>>> tweeter = Tweeter()
>>> tweeter.tweet(title="AI", use_palm=True, prompt="Create a 50 word description of what Artificial Intelligence is.")
Source code in twitternewsbot/tweeter.py
def tweet(self, title: str|None = None, tweet: str|None = None, articles_list: list|None = None, use_palm: bool = False, prompt: str|None = None) -> dict:
    """Tweet the given articles list

    Parameters
    ----------
    title : str, optional
        The title of the tweet
    tweet : str, optional
        The tweet to be posted
    articles_list : list, optional
        A list of dictionaries containing the title, source, link and text of the articles.
        Obtained from the NewsFinder.get_news_articles() method
    use_palm : bool, optional
        A boolean value indicating whether to use PaLM to generate tweet with prompt
    prompt : str, optional
        The prompt to be used for the summarization with PaLM

    Returns
    -------
    tweet : dict
        A dictionary containing the total character count, the number of tweets posted, and the id of the parent tweet

    Raises
    ------
    TypeError
        If title is not a string
    TypeError
        If tweet is not a string
    TypeError
        If articles_list is not a list of dictionaries
    TypeError
        If use_palm is not a boolean
    ValueError
        If use_palm is True and tweet or articles_list is not None
    ValueError
        If use_palm is True and prompt is None
    ValueError
        If tweet and articles_list are both not None
    TypeError
        If prompt is not a string

    Examples
    --------

    Provide a title and tweet to be posted:

    >>> from twitternewsbot.tweeter import Tweeter
    >>> tweeter = Tweeter()
    >>> tweeter.tweet(title="Hello World", tweet="Hello World!")

    Provide a title and articles list to be posted:

    >>> from twitternewsbot.tweeter import Tweeter
    >>> from twitternewsbot.newsfinder import NewsFinder
    >>> tweeter = Tweeter()
    >>> newsfinder = NewsFinder()
    >>> articles_list = newsfinder.get_news_articles(topic="Bitcoin", num_articles=5, article_text=True) # Article Text must be True if summarizing articles
    >>> tweeter.tweet(title="Bitcoin News", articles_list=articles_list)

    Provide a prompt to be used with PaLM to generate a tweet:

    >>> from twitternewsbot.tweeter import Tweeter
    >>> tweeter = Tweeter()
    >>> tweeter.tweet(title="AI", use_palm=True, prompt="Create a 50 word description of what Artificial Intelligence is.")
    """

    # Check if title is valid
    if title is not None and not isinstance(title, str):
        raise TypeError("title must be a string")

    # Check if tweet is valid
    if tweet is not None and not isinstance(tweet, str):
        raise TypeError("tweet must be a string")

    # Check if articles_list is valid
    if articles_list is not None and (not isinstance(articles_list, list) or not all(isinstance(article, dict) for article in articles_list)):
        raise TypeError("articles_list must be a list of dicts")

    # Check if use_palm is valid
    if use_palm is not None and not isinstance(use_palm, bool):
        raise TypeError("use_palm must be a boolean")

    # Check if use_palm is True, then tweet and articles_list must be None
    if use_palm is True and (tweet is not None or articles_list is not None):
        raise ValueError("tweet and articles_list must be None if use_palm is True")

    # Check if prompt is not provided and use_palm is True
    if prompt is None and use_palm is True:
        raise ValueError("prompt must be provided if use_palm is True")

    # Check if tweet and articles_list are both provided
    if tweet is not None and articles_list is not None:
        raise ValueError("Both tweet and articles_list cannot be provided")

    # Check if prompt is valid
    if prompt is not None and not isinstance(prompt, str):
        raise TypeError("prompt must be a string")

    if articles_list is not None:
        # Handle the articles list
        tweet = self.__handle_articles_list(articles_list, title, prompt)
    elif use_palm:
        # Generate a tweet using PaLM
        tweet = self.__generate_with_palm(prompt)
        if title is not None:
            tweet = f"{title}:\n\n{tweet}"

    return self.__tweet(tweet)