├── README.md ├── book.txt ├── book_manager.py ├── bot.py ├── requirements.txt └── tests.py /README.md: -------------------------------------------------------------------------------- 1 | # hello-world-bot 2 | A template for creating a Twitter bot that tweets lines of a text one at a time. 3 | 4 | This is a template for the Build-a-Bot workshop at PyDX Conf 2015 in Portland, OR. 5 | 6 | (or whoever happens to want to make their own Twitter bot for the first time) 7 | 8 | You'll need to add a text file to book.txt and a secrets.py file that contains a consumer_key, consumer_secret, access_key, and access_secret for authorization with Twitter. 9 | 10 | Detailed setup instructions are at https://tpinecone.gitbooks.io/build-a-bot-workshop/content/ 11 | 12 | An example lives at https://twitter.com/flatlandbot 13 | -------------------------------------------------------------------------------- /book.txt: -------------------------------------------------------------------------------- 1 | put a book here, there are lots of good ones at https://www.gutenberg.org/ -------------------------------------------------------------------------------- /book_manager.py: -------------------------------------------------------------------------------- 1 | import nltk # for sentence parsing 2 | nltk.download('punkt') # we're only getting the bare minimum of what we need from nltk 3 | book_file = 'book.txt' 4 | 5 | class BookManager: 6 | def __init__(self): 7 | text_file = open(book_file, 'r+') 8 | text_string = text_file.read() 9 | text_file.close() 10 | self._text_string = text_string 11 | 12 | def delete_message(self, message): 13 | text_file = open(book_file, 'r+') 14 | text_file.seek(0) 15 | text_file.write(self.text_without_message(message)) 16 | text_file.truncate() 17 | text_file.close() 18 | 19 | def first_sentence(self): 20 | tokenizer = nltk.data.load('tokenizers/punkt/english.pickle') 21 | sentences = tokenizer.tokenize(self._text_string) 22 | return sentences[0] 23 | 24 | def text_without_message(self, message): 25 | return self._text_string[len(message):len(self._text_string)] 26 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | import tweepy # for tweeting 2 | import secrets # shhhh 3 | from book_manager import BookManager # for getting sentences out of our book file 4 | 5 | def get_next_chunk(): 6 | # open text file 7 | book = BookManager() 8 | first_sentence = book.first_sentence() 9 | # tweet the whole sentence if it's short enough 10 | if len(first_sentence) <= 140: 11 | chunk = first_sentence 12 | # otherwise just print the first 140 characters 13 | else: 14 | chunk = first_sentence[0:140] 15 | 16 | # delete what we just tweeted from the text file 17 | book.delete_message(chunk) 18 | return chunk 19 | 20 | def tweet(message): 21 | auth = tweepy.OAuthHandler(secrets.consumer_key, secrets.consumer_secret) 22 | auth.set_access_token(secrets.access_token, secrets.access_token_secret) 23 | api = tweepy.API(auth) 24 | auth.secure = True 25 | print("Posting message {}".format(message)) 26 | api.update_status(status=message) 27 | 28 | if __name__ == '__main__': 29 | tweet(get_next_chunk()) 30 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | nltk==3.0.5 2 | oauthlib==1.0.3 3 | six==1.9.0 4 | tweepy==3.4.0 -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from bot import get_next_chunk 3 | import book_manager 4 | import tempfile 5 | import os 6 | from unittest.mock import patch 7 | 8 | class TestBot(unittest.TestCase): 9 | 10 | def test_get_next_chunk_returns_short_sentences(self): 11 | test_book = "this is the first line. this is the second line." 12 | test_file = tempfile.NamedTemporaryFile('r+', delete = False) 13 | test_file.write(test_book) 14 | test_file.close() 15 | with patch('book_manager.book_file', test_file.name): 16 | message = get_next_chunk() 17 | self.assertEqual(message, "this is the first line.") 18 | os.remove(test_file.name) 19 | 20 | def test_get_next_chunk_returns_segments_of_long_sentences(self): 21 | test_book = "this is the first line which is rather long and won't fit within the limits of a single tweet so we'll have to truncate it a little bit unfortunately. this is the second line." 22 | test_file = tempfile.NamedTemporaryFile('r+', delete = False) 23 | test_file.write(test_book) 24 | test_file.close() 25 | with patch('book_manager.book_file', test_file.name): 26 | message = get_next_chunk() 27 | self.assertEqual(message, "this is the first line which is rather long and won't fit within the limits of a single tweet so we'll have to truncate it a little bit unfo") 28 | os.remove(test_file.name) 29 | 30 | def test_get_next_chunk_deletes_message_from_book(self): 31 | test_book = "this is the first line. this is the second line." 32 | test_file = tempfile.NamedTemporaryFile('r+', delete = False) 33 | test_file.write(test_book) 34 | test_file.close() 35 | with patch('book_manager.book_file', test_file.name): 36 | message = get_next_chunk() 37 | new_contents = open(test_file.name, 'r+') 38 | self.assertEqual(new_contents.read(), ' this is the second line.') 39 | new_contents.close() 40 | os.remove(test_file.name) 41 | 42 | if __name__ == '__main__': 43 | unittest.main() 44 | --------------------------------------------------------------------------------