Skip to content

Commit

Permalink
Added IO adapter for communicating with users through twitter.
Browse files Browse the repository at this point in the history
  • Loading branch information
gunthercox committed Dec 1, 2015
1 parent ab89c89 commit 760ced2
Show file tree
Hide file tree
Showing 9 changed files with 561 additions and 19 deletions.
1 change: 1 addition & 0 deletions chatterbot/adapters/io/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .io import IOAdapter
from .terminal import TerminalAdapter
from .io_json import JsonAdapter
from .twitter_io import TwitterAdapter
from .no_output import NoOutputAdapter

205 changes: 205 additions & 0 deletions chatterbot/adapters/io/twitter_io.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
# -*- encoding: utf-8 -*-
from chatterbot.adapters.io import IOAdapter
from chatterbot.conversation import Statement
import twitter
import threading
import time

try:
from queue import Queue
except ImportError:
# Use the python 2 queue import
from Queue import Queue


class SimulatedAnnealingScheduler(object):
"""
This class implements a simulated annealing algorithm to determine
the correct schedule for running a function.
The benefit of this class is that it is more efficient than interval
checking when a given function may yield a greater probability of
returning similar consecutive results.
"""

def __init__(self, function, comparison_function, interval=5):
"""
Takes a function to be run periodically and a comparison function to
determine if the result of the function is true or false.
"""
self.function = function
self.check = comparison_function

self.interval = interval

# INTERVAL_MIN = 1 second
self.INTERVAL_MIN = 1

# INTERVAL_MAX = number of seconds in 1 day
self.INTERVAL_MAX = 60 * 60 * 24

self.INCREMENT_AMOUNT = 2
self.DECREMENT_AMOUNT = 2

self.thread = threading.Thread(target=self.start, args=())
self.thread.daemon = True
self.thread.start()

def get_temperature(self, scaling_factor=10):
pass

def decrease_interval(self):
"""
Decrement the interval as long as doing so will not cause it to
decrease past the predefined minimum.
"""
if (self.interval - self.DECREMENT_AMOUNT) >= self.INTERVAL_MIN:
self.interval -= self.DECREMENT_AMOUNT

def increase_interval(self):
"""
Increment the interval as long as doing so will not cause it to
increase past the predefined maximum.
"""
if (self.interval + self.INCREMENT_AMOUNT) <= self.INTERVAL_MAX:
self.interval += self.INCREMENT_AMOUNT

def start(self):
while True:
result = self.function()

if self.check(result):
self.increase_interval()
else:
self.decrease_interval()

time.sleep(self.interval)

def stop(self):
self.thread.stop()


class TwitterAdapter(IOAdapter):

def __init__(self, **kwargs):
super(TwitterAdapter, self).__init__(**kwargs)

self.api = twitter.Api(
consumer_key=kwargs["twitter_consumer_key"],
consumer_secret=kwargs["twitter_consumer_secret"],
access_token_key=kwargs["twitter_access_token_key"],
access_token_secret=kwargs["twitter_access_token_secret"]
)

self.mention_queue = Queue()
self.direct_message_queue = Queue()

self.message_checker = SimulatedAnnealingScheduler(
self.get_messages,
self.is_new_message
)

def post_update(self, message):
return self.api.PostUpdate(message)

def favorite(self, tweet_id):
return self.api.CreateFavorite(id=tweet_id)

def follow(self, username):
return self.api.CreateFriendship(screen_name=username)

def get_list_users(self, username, slug):
return self.api.GetListMembers(None, slug, owner_screen_name=username)

def get_mentions(self):
mentions = self.api.GetMentions()

print "get_mentions:", mentions
return mentions

def is_new_message(self, data):
print "message data:"
for d in data:
print "\t", d.text
print "\t", d

return True

def get_messages(self):
return self.api.GetDirectMessages(count=5)

def search(self, q, count=1, result_type="mixed"):
return self.api.GetSearch(term=q, count=count, result_type=result_type)

def get_related_messages(self, text):
results = search(text, count=50)
replies = []
non_replies = []

for result in results["statuses"]:

# Select only results that are replies
if result["in_reply_to_status_id_str"] is not None:
message = result["text"]
replies.append(message)

# Save a list of other results in case a reply cannot be found
else:
message = result["text"]
non_replies.append(message)

if len(replies) > 0:
return replies

return non_replies

def reply(self, tweet_id, message):
"""
Reply to a tweet
"""
return self.api.PostUpdate(message, in_reply_to_status_id=tweet_id)

def tweet_to_friends(self, username, slug, greetings, debug=False):
"""
Tweet one random message to the next friend in a list every hour.
The tweet will not be sent and will be printed to the console when in
debug mode.
"""
from time import time, sleep
from random import choice

# Get the list of robots
robots = self.get_list_users(username, slug=slug)

for robot in robots:
message = ("@" + robot + " " + choice(greetings)).strip("\n")

if debug is True:
print(message)
else:
sleep(3600-time() % 3600)
t.statuses.update(status=message)

def has_responeded_to_message(self, message_id):
# TODO
pass

def process_input(self):
"""
This method should check twitter for new mentions and
return them as Statement objects.
"""
# Download a list of recent mentions
mentions = self.get_mentions()

print "MENTIONS:", mentions

for mention in mentions:
mention = Statement(mention.text)

# Add the mention to the mention queue if a response has not been made
if not self.has_responeded_to_message(mention):
self.mention_queue.put(mention)

def process_response(self, input_statement):
return input_statement

1 change: 1 addition & 0 deletions examples/terminal_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@

except (KeyboardInterrupt, EOFError, SystemExit):
break

48 changes: 29 additions & 19 deletions examples/twitter_example.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,41 @@
from chatterbot import ChatBot
from settings import TWITTER
import time

'''
Respond to mentions on twitter.
The bot will follow the user who mentioned it and
favorite the post in which the mention was made.
The bot will respond to mentions and direct messages on twitter.
To use this example, create a new settings.py file.
Define the following in settings.py:
TWITTER = {}
TWITTER["CONSUMER_KEY"] = "your-twitter-public-key"
TWITTER["CONSUMER_SECRET"] = "your-twitter-sceret-key"
'''


chatbot = ChatBot("ChatterBot",
storage_adapter="chatterbot.adapters.storage.JsonDatabaseAdapter",
logic_adapter="chatterbot.adapters.logic.ClosestMatchAdapter",
io_adapter="chatterbot.adapters.io.TwitterAdapter",
database="../database.db")
database="../database.db",
twitter_consumer_key=TWITTER["CONSUMER_KEY"],
twitter_consumer_secret=TWITTER["CONSUMER_SECRET"],
twitter_access_token_key=TWITTER["ACCESS_TOKEN"],
twitter_access_token_secret=TWITTER["ACCESS_TOKEN_SECRET"]
)

for mention in chatbot.get_mentions():
time.sleep(200)

'''
Check to see if the post has been favorited
We will use this as a check for whether or not to respond to it.
Only respond to unfavorited mentions.
'''
'''
while True:
try:
user_input = chatbot.get_input()
if not mention["favorited"]:
screen_name = mention["user"]["screen_name"]
text = mention["text"]
response = chatbot.get_response(text)
bot_input = chatbot.get_response(user_input)
print(text)
print(response)
# Pause before checking for the next message
time.sleep(25)
chatbot.follow(screen_name)
chatbot.favorite(mention["id"])
chatbot.reply(mention["id"], response)
except (KeyboardInterrupt, EOFError, SystemExit):
break
'''
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ fuzzywuzzy>=0.8.0
jsondatabase>=0.0.7
nltk<4.0.0
pymongo>=3.0.3,<4.0.0
python-twitter>=2.2.0
22 changes: 22 additions & 0 deletions tests/io_adapter_tests/test_twitter_adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from unittest import TestCase
from chatterbot.adapters.io import TwitterAdapter


class TwitterAdapterTests(TestCase):
pass

'''
def setUp(self):
self.adapter = TwitterAdapter(
twitter_consumer_key="blahblahblah",
twitter_consumer_secret="nullvoidnullvoidnullvoid",
twitter_access_token_key="blahblahblah",
twitter_access_token_secret="nullvoidnullvoidnullvoid"
)
def test_get_mentions(self):
from.twitter_data.mentions import MENTIONS
mentions = self.adapter.get_mentions()
'''

Empty file.
Loading

0 comments on commit 760ced2

Please sign in to comment.