 3d0f7955b6
			
		
	
	
		3d0f7955b6
		
	
	
	
	
		
			
			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/.
		
			
				
	
	
		
			168 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #!/usr/bin/env python
 | |
| # -*- coding: utf-8 -*-
 | |
| #
 | |
| # Twitter 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, user):
 | |
|     # type: (ConfigParser, int, int) -> None
 | |
|     config.set('twitter', 'since_id', since_id)
 | |
|     config.set('twitter', 'user_id', user)
 | |
|     with open(CONFIGFILE, 'wb') as configfile:
 | |
|         config.write(configfile)
 | |
| 
 | |
| parser = optparse.OptionParser(r"""
 | |
| 
 | |
| %prog --user foo@example.com --api-key 0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5 --twitter-id twitter_handle --site=https://zulip.example.com
 | |
| 
 | |
|     Slurp tweets on your timeline into a specific zulip stream.
 | |
| 
 | |
|     Run this on your personal machine.  Your API key and twitter id
 | |
|     are revealed 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.
 | |
| 
 | |
|     Depends on: https://github.com/bear/python-twitter version 3.1
 | |
|     (`pip install python-twitter`)
 | |
| """)
 | |
| 
 | |
| parser.add_option('--twitter-id',
 | |
|                   help='Twitter username to poll for new tweets from"',
 | |
|                   metavar='URL')
 | |
| parser.add_option('--stream',
 | |
|                   help='Default zulip stream to write tweets to')
 | |
| parser.add_option('--limit-tweets',
 | |
|                   default=15,
 | |
|                   type='int',
 | |
|                   help='Maximum number of tweets to push at once')
 | |
| 
 | |
| parser.add_option_group(zulip.generate_option_group(parser))
 | |
| (options, args) = parser.parse_args()
 | |
| 
 | |
| if not options.twitter_id:
 | |
|     parser.error('You must specify --twitter-id')
 | |
| 
 | |
| 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 or not consumer_secret or not access_token_key or not access_token_secret:
 | |
|     parser.error("Please provide a ~/.zulip_twitterrc")
 | |
| 
 | |
| 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()
 | |
| 
 | |
| try:
 | |
|     since_id = config.getint('twitter', 'since_id')
 | |
| except NoOptionError:
 | |
|     since_id = -1
 | |
| 
 | |
| try:
 | |
|     user_id = config.get('twitter', 'user_id')
 | |
| except NoOptionError:
 | |
|     user_id = options.twitter_id
 | |
| 
 | |
| client = zulip.Client(
 | |
|     email=options.zulip_email,
 | |
|     api_key=options.zulip_api_key,
 | |
|     site=options.zulip_site,
 | |
|     client="ZulipTwitter/" + VERSION,
 | |
|     verbose=True)
 | |
| 
 | |
| if since_id < 0 or options.twitter_id != user_id:
 | |
|     # No since id yet, fetch the latest and then start monitoring from next time
 | |
|     # Or, a different user id is being asked for, so start from scratch
 | |
|     # Either way, fetch last 5 tweets to start off
 | |
|     statuses = api.GetUserTimeline(screen_name=options.twitter_id, count=5)
 | |
| else:
 | |
|     # We have a saved last id, so insert all newer tweets into the zulip stream
 | |
|     statuses = api.GetUserTimeline(screen_name=options.twitter_id, since_id=since_id)
 | |
| 
 | |
| for status in statuses[::-1][:options.limit_tweets]:
 | |
|     composed = "%s (%s)" % (status.user.name, status.user.screen_name)
 | |
|     message = {
 | |
|         "type": "stream",
 | |
|         "to": [options.stream],
 | |
|         "subject": composed,
 | |
|         "content": status.text,
 | |
|     }
 | |
| 
 | |
|     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, user_id)
 |