api: Move the API package to a dedicated subdirectory.
In order to keep all three packages (zulip, zulip_bots, zulip_botserver) in the same repo, all package files must now be nested one level deeper. For instance, python-zulip-api/zulip_bots/zulip_bots/bots/, instead of python-zulip-api/zulip_bots/bots/.
This commit is contained in:
parent
879f44ab3a
commit
3d0f7955b6
59 changed files with 186 additions and 192 deletions
194
zulip/integrations/twitter/twitter-search-bot
Executable file
194
zulip/integrations/twitter/twitter-search-bot
Executable file
|
@ -0,0 +1,194 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Twitter search integration for Zulip
|
||||
#
|
||||
# Copyright © 2014 Zulip, Inc.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import sys
|
||||
import optparse
|
||||
from six.moves.configparser import ConfigParser, NoSectionError, NoOptionError
|
||||
|
||||
import zulip
|
||||
VERSION = "0.9"
|
||||
CONFIGFILE = os.path.expanduser("~/.zulip_twitterrc")
|
||||
|
||||
def write_config(config, since_id):
|
||||
# type: (ConfigParser, int) -> None
|
||||
if 'search' not in config.sections():
|
||||
config.add_section('search')
|
||||
config.set('search', 'since_id', since_id)
|
||||
with open(CONFIGFILE, 'wb') as configfile:
|
||||
config.write(configfile)
|
||||
|
||||
parser = optparse.OptionParser(r"""
|
||||
|
||||
%prog --user username@example.com --api-key a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5 \
|
||||
--search="@nprnews,quantum physics"
|
||||
|
||||
Send Twitter search results to a Zulip stream.
|
||||
|
||||
Depends on: https://github.com/bear/python-twitter version 3.1
|
||||
|
||||
To use this script:
|
||||
|
||||
0. Use `pip install python-twitter` to install `python-twitter`.
|
||||
1. Set up Twitter authentication, as described below
|
||||
2. Subscribe to the stream that will receive Twitter updates (default stream: twitter)
|
||||
3. Test the script by running it manually, like this:
|
||||
|
||||
/usr/local/share/zulip/integrations/twitter/twitter-search-bot \
|
||||
--search="@nprnews,quantum physics" --site=https://zulip.example.com
|
||||
|
||||
4. Configure a crontab entry for this script. A sample crontab entry
|
||||
that will process tweets every 5 minutes is:
|
||||
|
||||
*/5 * * * * /usr/local/share/zulip/integrations/twitter/twitter-search-bot [options]
|
||||
|
||||
== Setting up Twitter authentications ==
|
||||
|
||||
Run this on a personal or trusted machine, because your API key is
|
||||
visible to local users through the command line or config file.
|
||||
|
||||
This bot uses OAuth to authenticate with Twitter. Please create a
|
||||
~/.zulip_twitterrc with the following contents:
|
||||
|
||||
[twitter]
|
||||
consumer_key =
|
||||
consumer_secret =
|
||||
access_token_key =
|
||||
access_token_secret =
|
||||
|
||||
In order to obtain a consumer key & secret, you must register a
|
||||
new application under your Twitter account:
|
||||
|
||||
1. Go to http://dev.twitter.com
|
||||
2. Log in
|
||||
3. In the menu under your username, click My Applications
|
||||
4. Create a new application
|
||||
|
||||
Make sure to go the application you created and click "create my
|
||||
access token" as well. Fill in the values displayed.
|
||||
""")
|
||||
|
||||
parser.add_option('--search',
|
||||
dest='search_terms',
|
||||
help='Terms to search on',
|
||||
action='store')
|
||||
parser.add_option('--stream',
|
||||
dest='stream',
|
||||
help='The stream to which to send tweets',
|
||||
default="twitter",
|
||||
action='store')
|
||||
parser.add_option('--limit-tweets',
|
||||
default=15,
|
||||
type='int',
|
||||
help='Maximum number of tweets to send at once')
|
||||
|
||||
parser.add_option_group(zulip.generate_option_group(parser))
|
||||
(opts, args) = parser.parse_args()
|
||||
|
||||
if not opts.search_terms:
|
||||
parser.error('You must specify a search term.')
|
||||
|
||||
try:
|
||||
config = ConfigParser()
|
||||
config.read(CONFIGFILE)
|
||||
|
||||
consumer_key = config.get('twitter', 'consumer_key')
|
||||
consumer_secret = config.get('twitter', 'consumer_secret')
|
||||
access_token_key = config.get('twitter', 'access_token_key')
|
||||
access_token_secret = config.get('twitter', 'access_token_secret')
|
||||
except (NoSectionError, NoOptionError):
|
||||
parser.error("Please provide a ~/.zulip_twitterrc")
|
||||
|
||||
if not (consumer_key and consumer_secret and access_token_key and access_token_secret):
|
||||
parser.error("Please provide a ~/.zulip_twitterrc")
|
||||
|
||||
try:
|
||||
since_id = config.getint('search', 'since_id')
|
||||
except (NoOptionError, NoSectionError):
|
||||
since_id = 0
|
||||
|
||||
try:
|
||||
import twitter
|
||||
except ImportError:
|
||||
parser.error("Please install twitter-python")
|
||||
|
||||
api = twitter.Api(consumer_key=consumer_key,
|
||||
consumer_secret=consumer_secret,
|
||||
access_token_key=access_token_key,
|
||||
access_token_secret=access_token_secret)
|
||||
|
||||
user = api.VerifyCredentials()
|
||||
|
||||
if not user.id:
|
||||
print("Unable to log in to twitter with supplied credentials. Please double-check and try again")
|
||||
sys.exit()
|
||||
|
||||
client = zulip.Client(
|
||||
email=opts.zulip_email,
|
||||
api_key=opts.zulip_api_key,
|
||||
site=opts.zulip_site,
|
||||
client="ZulipTwitterSearch/" + VERSION,
|
||||
verbose=True)
|
||||
|
||||
search_query = " OR ".join(opts.search_terms.split(","))
|
||||
statuses = api.GetSearch(search_query, since_id=since_id)
|
||||
|
||||
for status in statuses[::-1][:opts.limit_tweets]:
|
||||
# https://twitter.com/eatevilpenguins/status/309995853408530432
|
||||
composed = "%s (%s)" % (status.user.name,
|
||||
status.user.screen_name)
|
||||
url = "https://twitter.com/%s/status/%s" % (status.user.screen_name,
|
||||
status.id)
|
||||
content = status.text
|
||||
|
||||
search_term_used = None
|
||||
for term in opts.search_terms.split(","):
|
||||
if term.lower() in content.lower():
|
||||
search_term_used = term
|
||||
break
|
||||
# For some reason (perhaps encodings or message tranformations we
|
||||
# didn't anticipate), we don't know what term was used, so use a
|
||||
# default.
|
||||
if not search_term_used:
|
||||
search_term_used = "mentions"
|
||||
|
||||
message = {
|
||||
"type": "stream",
|
||||
"to": [opts.stream],
|
||||
"subject": search_term_used,
|
||||
"content": url,
|
||||
}
|
||||
|
||||
ret = client.send_message(message)
|
||||
|
||||
if ret['result'] == 'error':
|
||||
# If sending failed (e.g. no such stream), abort and retry next time
|
||||
print("Error sending message to zulip: %s" % ret['msg'])
|
||||
break
|
||||
else:
|
||||
since_id = status.id
|
||||
|
||||
write_config(config, since_id)
|
Loading…
Add table
Add a link
Reference in a new issue