diff --git a/Twitter/README.txt b/Twitter/README.txt new file mode 100644 index 0000000..d60b47a --- /dev/null +++ b/Twitter/README.txt @@ -0,0 +1 @@ +Insert a description of your plugin here, with any notes, etc. about using it. diff --git a/Twitter/__init__.py b/Twitter/__init__.py new file mode 100644 index 0000000..a70b931 --- /dev/null +++ b/Twitter/__init__.py @@ -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: diff --git a/Twitter/config.py b/Twitter/config.py new file mode 100644 index 0000000..2a5a406 --- /dev/null +++ b/Twitter/config.py @@ -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: diff --git a/Twitter/get_access_token.py b/Twitter/get_access_token.py new file mode 100755 index 0000000..0f7fe32 --- /dev/null +++ b/Twitter/get_access_token.py @@ -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 '' + diff --git a/Twitter/local/__init__.py b/Twitter/local/__init__.py new file mode 100644 index 0000000..e86e97b --- /dev/null +++ b/Twitter/local/__init__.py @@ -0,0 +1 @@ +# Stub so local is a module, used for third-party modules diff --git a/Twitter/plugin.py b/Twitter/plugin.py new file mode 100644 index 0000000..15b7711 --- /dev/null +++ b/Twitter/plugin.py @@ -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): + """[] [] + + Replies with the friends (i.e. people who one subscribes to) of the + , as the associated account. is only + needed if you don't run the command in the channel itself. If + 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: diff --git a/Twitter/test.py b/Twitter/test.py new file mode 100644 index 0000000..6624934 --- /dev/null +++ b/Twitter/test.py @@ -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: