diff --git a/.gitignore b/.gitignore
index 325ad527a..8b730eca8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,7 +10,7 @@ env
0/
tests/.cache
.coverage
-.cache
docs/_build
docs/_static
-docs/_templates
\ No newline at end of file
+docs/_templates
+/api-token.txt
diff --git a/.travis.yml b/.travis.yml
index f8b358de7..84de22f21 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,13 +1,14 @@
sudo: false
+os:
+ - linux
language: python
python:
- "3.5"
env:
- matrix:
- - TOX_ENV=py27
- - TOX_ENV=py34
- - TOX_ENV=py35
- - TOX_ENV=flake8
+ matrix:
+ - TOX_ENV=py27
+ - TOX_ENV=py34
+ - TOX_ENV=py35
cache: pip
install:
- "travis_retry pip install setuptools --upgrade"
diff --git a/Procfile b/Procfile
new file mode 100644
index 000000000..91803f8d6
--- /dev/null
+++ b/Procfile
@@ -0,0 +1,2 @@
+heroku ps:scale worker=1
+worker: python experibot.py
\ No newline at end of file
diff --git a/README.md b/README.md
index 820a4270f..eb3daaa5b 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,29 @@
-python-slackclient
+BOTIN
================
+[](https://travis-ci.org/slackhq/python-slackclient)
+
+Botin (Spanish for "little bot")
+
+This is a small bot used by a slack community of friends that have been communicating with each other via computers since the days of BBSes.
+
+Derived from slackhq/python-slackclient (Their README.md is below)
+
+
+
+Commands implemented:
+- help: To learn its commands.
+- imdb: Search for a movie
+- imdbs: Search for similarly-titled movies
+- imdbtt: Search for a movie given an IMDB ID (ttcode)
+
+Uses omdb.py and TMDB.
+
+
+
+python-slackclient
+-----------------
+
[](https://travis-ci.org/slackhq/python-slackclient)
[](https://coveralls.io/github/slackhq/python-slackclient?branch=master)
[](http://python-slackclient.readthedocs.io/en/latest/?badge=latest)
diff --git a/bot_commands.py b/bot_commands.py
new file mode 100644
index 000000000..2bfe9cc37
--- /dev/null
+++ b/bot_commands.py
@@ -0,0 +1,132 @@
+import os
+
+import omdb
+import requests
+
+
+def imdb_info(input_text):
+ message_list = []
+ if len(input_text) == 0:
+ text = "Command format: imdb
[ ## ]"
+ message_list.append((text, []))
+ else:
+ text_l = input_text.split("##")
+ if len(text_l) == 1:
+ # title only
+ om = omdb.title(text_l[0], tomatoes=True)
+ else:
+ # title and year
+ om = omdb.title(text_l[0], year=text_l[1], tomatoes=True)
+
+ if "title" in om.keys():
+ message_list = output_movie(input_text, om)
+ else:
+ text = "Sorry, I can't seem to find anything for " + input_text
+ message_list.append((text, []))
+ return message_list
+
+
+def imdb_by_code(input_text):
+ message_list = []
+ if len(input_text) == 0:
+ text = "Command format: imdbtt "
+ message_list.append((text, []))
+ else:
+ om = omdb.imdbid(input_text, tomatoes=True)
+ if "title" in om.keys():
+ message_list = output_movie(input_text, om)
+ else:
+ text = "Sorry, " + input_text + " doesn't seem to be valid."
+ message_list.append((text, []))
+ return message_list
+
+
+def output_movie(input_text, om):
+ OMDB_API = os.environ.get("OMDB_API")
+ output = []
+ text = 'This is what I found for "' + input_text + '":'
+ attach = list([dict(title=om.title,
+ title_link="http://www.imdb.com/title/" + om.imdb_id,
+ thumb_url="http://img.omdbapi.com/?apikey=" + OMDB_API + "&i=" + om.imdb_id,
+ text=om.plot,
+ fields=list([dict(title="Released", value=om.released, short=True),
+ dict(title="Runtime", value=om.runtime, short=True),
+ dict(title="Actors", value=om.actors, short=True),
+ dict(title="Rating", value=format_rating(om),
+ short=True)
+ ])
+ )
+ ])
+ output.append((text, attach))
+ text = get_trailer(om.imdb_id)
+ output.append((text, []))
+ return output
+
+
+def get_trailer(imdb_id):
+ trailer_info = "No trailer found."
+ TMDB_API = os.environ.get("TMDB_API")
+ find_movie_url = 'https://api.themoviedb.org/3/find/{id}?api_key={api}&language=en-US&external_source=imdb_id'. \
+ format(id=imdb_id, api=TMDB_API)
+ t = _GET(find_movie_url)
+ if t["movie_results"]:
+ tmdb_movie_id = t['movie_results'][0]['id']
+ get_trailer_url = 'https://api.themoviedb.org/3/movie/{id}/videos?api_key={api}&language=en-US'.format(
+ id=tmdb_movie_id, api=TMDB_API)
+ t = _GET(get_trailer_url)
+ if t['results']:
+ latest_trailer_key = t['results'][0]['key']
+ trailer_info = "".join(["<",
+ "http://www.youtube.com/watch?v={yt_key}".format(yt_key=latest_trailer_key),
+ ">"])
+ return trailer_info
+
+
+def _GET(path):
+ return _request('GET', path)
+
+
+def _request(method, path):
+ response = requests.request(method, path)
+ response.raise_for_status()
+ response.encoding = 'utf-8'
+ return response.json()
+
+
+def format_rating(item):
+ if item.imdb_rating == "N/A":
+ imdb_portion = "N/A "
+ else:
+ imdb_portion = "IMDB: " + item.imdb_rating + "/10 (" + item.imdb_votes + ") "
+
+ if "tomato_meter" in item.keys():
+ tomato_portion = "\nTomato: " + item.tomato_meter + "% (" + item.tomato_reviews + ")"
+ else:
+ tomato_portion = ""
+
+ return imdb_portion + tomato_portion
+
+
+def imdb_search(input_text):
+ OMDB_API = os.environ.get("OMDB_API")
+ message_list = []
+ if len(input_text) == 0:
+ text = "Command format: imdbs [ ## ]"
+ message_list.append((text, []))
+ else:
+ options_list = input_text.split("##")
+ # print options_list
+ if len(options_list) > 1:
+ om = omdb.search(options_list[0], page=options_list[1].strip())
+ else:
+ om = omdb.search(input_text)
+ mn = min(len(om), 10)
+ text = "Here's what I found: "
+ attach = []
+ d = dict()
+ for i in range(mn):
+ item = om[i]
+ d["title"] = (d["title"] if "title" in d.keys() else "") + item.title + " (" + item.year + ") \n"
+ attach.append(d)
+ message_list.append((text, attach))
+ return message_list
diff --git a/bot_commands_test.py b/bot_commands_test.py
new file mode 100644
index 000000000..2a7efdb61
--- /dev/null
+++ b/bot_commands_test.py
@@ -0,0 +1,8 @@
+def imdbInfo_test():
+ # return c.imdbInfo("Mighty Ducks")
+ return ""
+
+
+if __name__ == "__main__":
+ # print imdbInfo_test()
+ p = None
diff --git a/command_caller.py b/command_caller.py
new file mode 100644
index 000000000..283a2732c
--- /dev/null
+++ b/command_caller.py
@@ -0,0 +1,20 @@
+import bot_commands as c
+
+
+def call_imdb_info(i):
+ print(c.imdb_info(i))
+
+
+def call_imdb_by_code(c):
+ print(c.imdb_by_code(c))
+
+
+def call_imdb_search(s):
+ print(c.imdb_search(s))
+
+
+def test_each_output():
+ print("imdb Fantastic Four ## 1994")
+ call_imdb_info("Fantastic Four ## 1994")
+ print("imdb white comanche")
+ call_imdb_info("white comanche")
diff --git a/experibot.py b/experibot.py
new file mode 100644
index 000000000..57217f7df
--- /dev/null
+++ b/experibot.py
@@ -0,0 +1,111 @@
+import argparse
+import os
+import time
+import traceback
+from collections import namedtuple
+
+import bot_commands as c
+import command_caller as a
+from slackclient import SlackClient
+
+command_struct = namedtuple("command_struct", ["func", "description"])
+
+# starterbot's ID as an environment variable
+BOT_ID = os.environ.get("BOT_ID")
+
+
+def bothelp(text):
+ message_list = []
+ text = "Here's everything I can do:"
+ attach = []
+ fields = []
+ for key, available_command in COMMANDS.items():
+ fields.append(dict(title=key, value=available_command.description))
+ attach.append(dict(fields=fields))
+ message_list.append((text, attach))
+ return message_list
+
+
+# constants
+AT_BOT = "<@" + BOT_ID + "> "
+COMMANDS = {"help": command_struct(bothelp, "This stuff, yeah."),
+ "imdb": command_struct(c.imdb_info, "Get information on a specific movie."),
+ "imdbs": command_struct(c.imdb_search, "Search information on movies."),
+ "imdbtt": command_struct(c.imdb_by_code, "Get information on a movie by providing an IMDB ID.")
+ }
+
+UNABLE_TO_UNDERSTAND = "help"
+
+# instantiate Slack & Twilio clients
+slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))
+
+
+def handle_command(comm, chan):
+ """
+ Receives commands directed at the bot and determines if they
+ are valid commands. If so, then acts on the commands. If not,
+ returns back what it needs for clarification.
+ """
+ response = dict()
+ t = "Not sure what you mean. Use the *" + UNABLE_TO_UNDERSTAND + \
+ "* command for details on what I can do."
+ attach = []
+ received_command = comm.split(" ")[0]
+ if received_command in COMMANDS.keys():
+ try:
+ # Implementing message list, to allow bot to send multiple messages at once
+ message_list = COMMANDS[received_command].func(" ".join(comm.split(" ")[1:]))
+ for (t, attach) in message_list:
+ # print t, attach
+ slack_client.api_call("chat.postMessage", channel=chan,
+ text=t,
+ attachments=attach,
+ as_user=True)
+ except:
+ t = "Command text `" + comm + "` resulted in the following exception: "
+ t += traceback.format_exc()
+ slack_client.api_call("chat.postMessage", channel="#botin_test",
+ text=t,
+ attachments=None,
+ as_user=True)
+ tc = "Sorry, error encountered with command " + comm
+ tc += ". Diagnostics printed to #botin_test."
+ slack_client.api_call("chat.postMessage", channel="#botin_test",
+ text=tc,
+ attachments=None,
+ as_user=True)
+
+
+def parse_slack_output(slack_rtm_output):
+ """
+ The Slack Real Time Messaging API is an events firehose.
+ this parsing function returns None unless a message is
+ directed at the Bot, based on its ID.
+ """
+ output_list = slack_rtm_output
+ if output_list and len(output_list) > 0:
+ for output in output_list:
+ if output and 'text' in output and AT_BOT in output['text']:
+ # return text after the @ mention, whitespace removed
+ return output['text'].split(AT_BOT)[1].strip(), \
+ output['channel']
+ return None, None
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description="experimental imdb slackbot with local abilities")
+ parser.add_argument("--loc", choices=["local", "slack"], default="slack")
+ args = vars(parser.parse_args())
+ if "loc" in args.keys() and args["loc"] == "local":
+ a.test_each_output()
+ else:
+ READ_WEBSOCKET_DELAY = 1 # 1 second delay between reading from firehose
+ if slack_client.rtm_connect():
+ # print("StarterBot connected and running!")
+ while True:
+ command, channel = parse_slack_output(slack_client.rtm_read())
+ if command and channel:
+ handle_command(command, channel)
+ time.sleep(READ_WEBSOCKET_DELAY)
+ else:
+ print("Connection failed. Invalid Slack token or bot ID?")
diff --git a/print_bot_id.py b/print_bot_id.py
new file mode 100644
index 000000000..b7ae916d0
--- /dev/null
+++ b/print_bot_id.py
@@ -0,0 +1,22 @@
+import os
+
+from slackclient import SlackClient
+
+BOT_NAME = 'botin'
+
+slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))
+print(os.environ.get('SLACK_BOT_TOKEN'))
+
+if __name__ == "__main__":
+ print(slack_client.api_call("api.test"))
+ api_call = slack_client.api_call("users.list")
+ print(api_call.get('ok'))
+ if api_call.get('ok'):
+ # retrieve all users so we can find our bot
+ users = api_call.get('members')
+ for user in users:
+ if 'name' in user and user.get('name') == BOT_NAME:
+ print("Bot ID for '" + user['name'] + "' is " + user.get('id'))
+ else:
+ print(api_call.get('error'))
+ print("could not find bot user with the name " + BOT_NAME)
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 87864e5f5..656f53bec 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,6 +1,6 @@
coveralls==1.1
ipdb==0.9.3
-ipython==4.1.2
+ipython==5.1.0
pdbpp==0.8.3
pytest>=2.8.2
pytest-mock>=1.1
diff --git a/requirements.txt b/requirements.txt
index 0b8229fe3..4b402e97e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,44 @@
-future==0.15.2
+backports-abc>=0.4
+backports.shutil-get-terminal-size==1.0.0
+backports.ssl-match-hostname==3.4.0.2
+colorama==0.3.7
+configparser==3.5.0
+decorator==4.0.10
+functools32==3.2.3.post2; python_version == "2.7"
+gunicorn==19.6.0
+ipykernel>=4.4.1
+ipython==5.1.0
+ipython-genutils==0.1.0
+ipywidgets>=4.1.1
+Jinja2==2.8
+jsonschema==2.5.1
+jupyter==1.0.0
+jupyter-client>=4.3.0
+jupyter-console==5.0.0
+jupyter-core>=4.1.1
+MarkupSafe==0.23
+mistune>=0.7.2
+nbconvert>=4.2.0
+nbformat>=4.0.1
+notebook>=4.2.2
+omdb==0.7.0
+path.py>=8.2
+pathlib2==2.1.0
+pickleshare>=0.7.3
+prompt-toolkit>=1.0.3
+Pygments==2.1.3
+# Python-contrib-nbextensions>==alpha
+pyzmq>=15.4.0
+qtconsole==4.2.1
+requests>=2.11.0
+simplegeneric==0.8.1
+singledispatch==3.4.0.3
six==1.10.0
-websocket-client==0.35.0
+slackclient==1.0.1
+tornado>=4.4.1
+tmdbsimple==1.4.0
+traitlets>=4.2.2
+wcwidth==0.1.7
+websocket-client==0.37.0
+win-unicode-console==0.5
+WTForms>=2.0.2
diff --git a/setup.py b/setup.py
index 5d6429e9b..254ab53d0 100644
--- a/setup.py
+++ b/setup.py
@@ -13,4 +13,9 @@
'requests',
'six',
],
+ extras_require={
+ ':python_version == "2.7"': [
+ 'functools32',
+ ]
+ },
zip_safe=False)
diff --git a/tox.ini b/tox.ini
index 822e858c7..92d9c5f22 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,13 +1,8 @@
[tox]
envlist=
- py{27,34,35},
- flake8
+ py{27,34,35}
skipsdist=true
-[flake8]
-max-line-length= 100
-exclude= tests/*
-
[testenv]
passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH
commands =
@@ -21,10 +16,3 @@ basepython =
py27: python2.7
py34: python3.4
py35: python3.5
-
-[testenv:flake8]
-basepython=python
-deps=flake8
-commands=
- flake8 \
- {toxinidir}/slackclient
\ No newline at end of file