Twitter: First commit (can authenticate and get friendslist)
parent
c7c04b2c57
commit
0f6a385921
|
@ -0,0 +1 @@
|
||||||
|
Insert a description of your plugin here, with any notes, etc. about using it.
|
|
@ -0,0 +1,69 @@
|
||||||
|
###
|
||||||
|
# Copyright (c) 2011, Valentin Lorentz
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# * Neither the name of the author of this software nor the name of
|
||||||
|
# contributors to this software may be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written consent.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
"""
|
||||||
|
Add a description of the plugin (to be presented to the user inside the wizard)
|
||||||
|
here. This should describe *what* the plugin does.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import supybot
|
||||||
|
import supybot.world as world
|
||||||
|
|
||||||
|
# Use this for the version of this plugin. You may wish to put a CVS keyword
|
||||||
|
# in here if you're keeping the plugin in CVS or some similar system.
|
||||||
|
__version__ = ""
|
||||||
|
|
||||||
|
# XXX Replace this with an appropriate author or supybot.Author instance.
|
||||||
|
if not hasattr(supybot.authors, 'progval'):
|
||||||
|
supybot.authors.progval =supybot.Author('Valentin Lorentz', 'ProgVal',
|
||||||
|
'progval@gmail.com')
|
||||||
|
__author__ = supybot.authors.progval
|
||||||
|
|
||||||
|
# This is a dictionary mapping supybot.Author instances to lists of
|
||||||
|
# contributions.
|
||||||
|
__contributors__ = {}
|
||||||
|
|
||||||
|
# This is a url where the most recent plugin package can be downloaded.
|
||||||
|
__url__ = '' # 'http://supybot.com/Members/yourname/Twitter/download'
|
||||||
|
|
||||||
|
import config
|
||||||
|
import plugin
|
||||||
|
reload(plugin) # In case we're being reloaded.
|
||||||
|
# Add more reloads here if you add third-party modules and want them to be
|
||||||
|
# reloaded when this plugin is reloaded. Don't forget to import them as well!
|
||||||
|
|
||||||
|
if world.testing:
|
||||||
|
import test
|
||||||
|
|
||||||
|
Class = plugin.Class
|
||||||
|
configure = config.configure
|
||||||
|
|
||||||
|
|
||||||
|
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
|
@ -0,0 +1,71 @@
|
||||||
|
###
|
||||||
|
# Copyright (c) 2011, Valentin Lorentz
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# * Neither the name of the author of this software nor the name of
|
||||||
|
# contributors to this software may be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written consent.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
import supybot.conf as conf
|
||||||
|
import supybot.registry as registry
|
||||||
|
from supybot.i18n import PluginInternationalization, internationalizeDocstring
|
||||||
|
|
||||||
|
_ = PluginInternationalization('Twitter')
|
||||||
|
|
||||||
|
def configure(advanced):
|
||||||
|
# This will be called by supybot to configure this module. advanced is
|
||||||
|
# a bool that specifies whether the user identified himself as an advanced
|
||||||
|
# user or not. You should effect your configuration by manipulating the
|
||||||
|
# registry as appropriate.
|
||||||
|
from supybot.questions import expect, anything, something, yn
|
||||||
|
conf.registerPlugin('Twitter', True)
|
||||||
|
|
||||||
|
|
||||||
|
Twitter = conf.registerPlugin('Twitter')
|
||||||
|
# This is where your configuration variables (if any) should go. For example:
|
||||||
|
# conf.registerGlobalValue(Twitter, 'someConfigVariableName',
|
||||||
|
# registry.Boolean(False, _("""Help for someConfigVariableName.""")))
|
||||||
|
conf.registerGroup(Twitter, 'accounts')
|
||||||
|
|
||||||
|
helpGetToken = _('running get_access_token.py is a way to get it')
|
||||||
|
|
||||||
|
conf.registerGroup(Twitter.accounts, 'bot')
|
||||||
|
conf.registerGlobalValue(Twitter.accounts.bot, 'key',
|
||||||
|
registry.String('', _("""The Twitter Access Token key for the bot's
|
||||||
|
account (%s)""") % helpGetToken))
|
||||||
|
conf.registerGlobalValue(Twitter.accounts.bot, 'secret',
|
||||||
|
registry.String('', _("""The Twitter Access Token secret for the bot's
|
||||||
|
account (%s)""") % helpGetToken, private=True))
|
||||||
|
|
||||||
|
conf.registerGroup(Twitter.accounts, 'channel')
|
||||||
|
conf.registerChannelValue(Twitter.accounts.channel, 'key',
|
||||||
|
registry.String('', _("""The Twitter Access Token key for this
|
||||||
|
channel's account (%s)""") % helpGetToken))
|
||||||
|
conf.registerChannelValue(Twitter.accounts.channel, 'secret',
|
||||||
|
registry.String('', _("""The Twitter Access Token secret for this
|
||||||
|
channel's account (%s)""") % helpGetToken, private=True))
|
||||||
|
|
||||||
|
|
||||||
|
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
|
@ -0,0 +1,90 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
#
|
||||||
|
# Copyright 2007 The Python-Twitter Developers
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# parse_qsl moved to urlparse module in v2.6
|
||||||
|
try:
|
||||||
|
from urlparse import parse_qsl
|
||||||
|
except:
|
||||||
|
from cgi import parse_qsl
|
||||||
|
|
||||||
|
import oauth2 as oauth
|
||||||
|
|
||||||
|
REQUEST_TOKEN_URL = 'https://api.twitter.com/oauth/request_token'
|
||||||
|
ACCESS_TOKEN_URL = 'https://api.twitter.com/oauth/access_token'
|
||||||
|
AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authorize'
|
||||||
|
SIGNIN_URL = 'https://api.twitter.com/oauth/authenticate'
|
||||||
|
|
||||||
|
consumer_key = 'bItq1HZhBGyx5Y8ardIeQ'
|
||||||
|
consumer_secret = 'qjC6Ye6xSMM3XPLR3LLeMqOP4ri0rgoYFT2si1RpY'
|
||||||
|
|
||||||
|
if consumer_key is None or consumer_secret is None:
|
||||||
|
print 'You need to edit this script and provide values for the'
|
||||||
|
print 'consumer_key and also consumer_secret.'
|
||||||
|
print ''
|
||||||
|
print 'The values you need come from Twitter - you need to register'
|
||||||
|
print 'as a developer your "application". This is needed only until'
|
||||||
|
print 'Twitter finishes the idea they have of a way to allow open-source'
|
||||||
|
print 'based libraries to have a token that can be used to generate a'
|
||||||
|
print 'one-time use key that will allow the library to make the request'
|
||||||
|
print 'on your behalf.'
|
||||||
|
print ''
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
signature_method_hmac_sha1 = oauth.SignatureMethod_HMAC_SHA1()
|
||||||
|
oauth_consumer = oauth.Consumer(key=consumer_key, secret=consumer_secret)
|
||||||
|
oauth_client = oauth.Client(oauth_consumer)
|
||||||
|
|
||||||
|
print 'Requesting temp token from Twitter'
|
||||||
|
|
||||||
|
resp, content = oauth_client.request(REQUEST_TOKEN_URL, 'GET')
|
||||||
|
|
||||||
|
if resp['status'] != '200':
|
||||||
|
print 'Invalid respond from Twitter requesting temp token: %s' % resp['status']
|
||||||
|
else:
|
||||||
|
request_token = dict(parse_qsl(content))
|
||||||
|
|
||||||
|
print ''
|
||||||
|
print 'Please visit this Twitter page and retrieve the pincode to be used'
|
||||||
|
print 'in the next step to obtaining an Authentication Token:'
|
||||||
|
print ''
|
||||||
|
print '%s?oauth_token=%s' % (AUTHORIZATION_URL, request_token['oauth_token'])
|
||||||
|
print ''
|
||||||
|
|
||||||
|
pincode = raw_input('Pincode? ')
|
||||||
|
|
||||||
|
token = oauth.Token(request_token['oauth_token'], request_token['oauth_token_secret'])
|
||||||
|
token.set_verifier(pincode)
|
||||||
|
|
||||||
|
print ''
|
||||||
|
print 'Generating and signing request for an access token'
|
||||||
|
print ''
|
||||||
|
|
||||||
|
oauth_client = oauth.Client(oauth_consumer, token)
|
||||||
|
resp, content = oauth_client.request(ACCESS_TOKEN_URL, method='POST', body='oauth_verifier=%s' % pincode)
|
||||||
|
access_token = dict(parse_qsl(content))
|
||||||
|
|
||||||
|
if resp['status'] != '200':
|
||||||
|
print 'The request for a Token did not succeed: %s' % resp['status']
|
||||||
|
print access_token
|
||||||
|
else:
|
||||||
|
print 'Your Twitter Access Token key: %s' % access_token['oauth_token']
|
||||||
|
print ' Access Token secret: %s' % access_token['oauth_token_secret']
|
||||||
|
print ''
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
# Stub so local is a module, used for third-party modules
|
|
@ -0,0 +1,110 @@
|
||||||
|
###
|
||||||
|
# Copyright (c) 2011, Valentin Lorentz
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# * Neither the name of the author of this software nor the name of
|
||||||
|
# contributors to this software may be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written consent.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
import twitter
|
||||||
|
import supybot.utils as utils
|
||||||
|
from supybot.commands import *
|
||||||
|
import supybot.plugins as plugins
|
||||||
|
import supybot.ircutils as ircutils
|
||||||
|
import supybot.callbacks as callbacks
|
||||||
|
from supybot.i18n import PluginInternationalization, internationalizeDocstring
|
||||||
|
|
||||||
|
_ = PluginInternationalization('Twitter')
|
||||||
|
|
||||||
|
reload(twitter)
|
||||||
|
if twitter.__version__.split('.') < ['0', '8', '0']:
|
||||||
|
raise ImportError('You current version of python-twitter is to old, '
|
||||||
|
'you need at least version 0.8.0, because older '
|
||||||
|
'versions do not support OAuth authentication.')
|
||||||
|
|
||||||
|
@internationalizeDocstring
|
||||||
|
class Twitter(callbacks.Plugin):
|
||||||
|
"""Add the help for "@plugin help Twitter" here
|
||||||
|
This should describe *how* to use this plugin."""
|
||||||
|
threaded = True
|
||||||
|
|
||||||
|
def __init__(self, irc):
|
||||||
|
self.__parent = super(Twitter, self)
|
||||||
|
callbacks.Plugin.__init__(self, irc)
|
||||||
|
self._apis = {}
|
||||||
|
|
||||||
|
def _getApi(self, channel):
|
||||||
|
if channel in self._apis:
|
||||||
|
return self._apis[channel]
|
||||||
|
if channel is None:
|
||||||
|
key = self.registryValue('accounts.bot.key')
|
||||||
|
secret = self.registryValue('accounts.bot.secret')
|
||||||
|
else:
|
||||||
|
key = self.registryValue('accounts.channel.key', channel)
|
||||||
|
secret = self.registryValue('accounts.channel.secret', channel)
|
||||||
|
if key == '' or secret == '':
|
||||||
|
return twitter.Api()
|
||||||
|
api = twitter.Api(consumer_key='bItq1HZhBGyx5Y8ardIeQ',
|
||||||
|
consumer_secret='qjC6Ye6xSMM3XPLR3LLeMqOP4ri0rgoYFT2si1RpY',
|
||||||
|
access_token_key=key,
|
||||||
|
access_token_secret=secret)
|
||||||
|
self._apis[channel] = api
|
||||||
|
return api
|
||||||
|
|
||||||
|
|
||||||
|
@internationalizeDocstring
|
||||||
|
def friendslist(self, irc, msg, args, channel, user):
|
||||||
|
"""[<channel>] [<user>]
|
||||||
|
|
||||||
|
Replies with the friends (i.e. people who one subscribes to) of the
|
||||||
|
<user>, as the <channel> associated account. <channel> is only
|
||||||
|
needed if you don't run the command in the channel itself. If <user>
|
||||||
|
is not given, it defaults to the channel's account."""
|
||||||
|
api = self._getApi(channel)
|
||||||
|
if not api._oauth_consumer and user is None:
|
||||||
|
irc.error(_('No account is associated with this channel. Ask '
|
||||||
|
'an op, try with another channel, or provide '
|
||||||
|
'a user name.'))
|
||||||
|
return
|
||||||
|
friends = api.GetFriends(user) # If user is not given, it defaults
|
||||||
|
# to None, and giving None to
|
||||||
|
# GetFriends() has the expected
|
||||||
|
# behaviour.
|
||||||
|
reply = utils.str.format("%L", ['%s(%s)' % (x.name, x.screen_name)
|
||||||
|
for x in friends])
|
||||||
|
reply = reply.encode('utf8')
|
||||||
|
irc.reply(reply)
|
||||||
|
friendslist = wrap(friendslist, ['channel',
|
||||||
|
optional('somethingWithoutSpaces')])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def die(self):
|
||||||
|
self.__parent.die()
|
||||||
|
|
||||||
|
Class = Twitter
|
||||||
|
|
||||||
|
|
||||||
|
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
|
|
@ -0,0 +1,37 @@
|
||||||
|
###
|
||||||
|
# Copyright (c) 2011, Valentin Lorentz
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# * Neither the name of the author of this software nor the name of
|
||||||
|
# contributors to this software may be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written consent.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
from supybot.test import *
|
||||||
|
|
||||||
|
class TwitterTestCase(ChannelPluginTestCase):
|
||||||
|
plugins = ('Twitter',)
|
||||||
|
|
||||||
|
|
||||||
|
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
Loading…
Reference in New Issue