├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── birdseed.py ├── setup.cfg ├── setup.py ├── tests ├── __init__.py └── test_birdseed.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | # general things to ignore 2 | .DS_STORE 3 | build/ 4 | dist/ 5 | *.egg-info/ 6 | *.egg 7 | *.py[cod] 8 | __pycache__/ 9 | *.so 10 | *~ 11 | 12 | # due to using tox and pytest 13 | .tox 14 | .cache -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ryan McDermott 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanmcdermott/birdseed/88a56224a7f831eae509e6944643ba14d3aa77cb/MANIFEST.in -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Birdseed 2 | ## What is it? 3 | `birdseed` is a utility to create pseudo and/or "real" random numbers from tweets based on a particular search query over Twitter's API. Use Twitter's noise to your advantage! 4 | 5 | ## What else? 6 | **This is for fun. It's not secure. Don't use it in production :)** 7 | 8 | It can be run in two modes: `real=True` and `real=False`. Real mode is default. 9 | 10 | When run in real mode, `birdseed` will get up to 100 tweets for a particular search query, compute a hash for each tweet, and store these in a list. When the user calls `birdseed_instance.random()` the last hash will be popped off the hashes list. When the list of hashes is 0, Twitter's API will be called again for the given search query. *There's no guarantee though that tweets coming back are new and were not previously hashed and popped off. Ideally, use a search query with lots of entropy (something short like a single character: 'a', 'e', 'i', 'o', 'u')* 11 | 12 | When run in non-real (pseudo) mode, `birdseed` will seed Python's random number generator with the first tweet it finds for the given search query. 13 | 14 | The hash algorithm used is SHA224, with an input vector of the text of the tweet, the Twitter handle, and the timestamp. 15 | 16 | ## Requirements 17 | Python 2.7+ or Python 3.3+, pip, and Twitter App credentials 18 | 19 | ## Installation 20 | `pip install birdseed` 21 | 22 | ## Usage 23 | ```python 24 | from __future__ import print_function 25 | 26 | import birdseed 27 | query = 'donald trump' 28 | access_key = 'YOUR_ACCESS_KEY' 29 | access_secret = 'YOUR_ACCESS_SECRET' 30 | consumer_key = 'YOUR_CONSUMER_KEY' 31 | consumer_secret = 'YOUR_CONSUMER_SECRET' 32 | 33 | t = birdseed.Birdseed(query, access_key, access_secret, consumer_key, consumer_secret) 34 | print(t.random()) 35 | ``` 36 | 37 | ## Contributing 38 | Pull requests are much appreciated and accepted. 39 | 40 | ## License 41 | Released under the [MIT License](http://www.opensource.org/licenses/MIT) 42 | -------------------------------------------------------------------------------- /birdseed.py: -------------------------------------------------------------------------------- 1 | """ 2 | Birdseed is a utility to create pseudo and/or "real" random numbers from tweets 3 | based on a particular search query over Twitter's API. Use Twitter's noise to 4 | your advantage! 5 | 6 | This is for fun, it's not secure. Don't use it in production. :) 7 | """ 8 | 9 | __author__ = "Ryan McDermott (ryan.mcdermott@ryansworks.com)" 10 | __version__ = "0.2.4" 11 | __copyright__ = "Copyright (c) 2015 Ryan McDermott" 12 | __license__ = "MIT" 13 | 14 | import twitter 15 | import hashlib 16 | import random 17 | 18 | 19 | class Birdseed(): 20 | def __init__(self, query, access_key, access_secret, consumer_key, 21 | consumer_secret, real=True): 22 | self.access_key = access_key 23 | self.access_secret = access_secret 24 | self.consumer_key = consumer_key 25 | self.consumer_secret = consumer_secret 26 | self.real = real 27 | self.query = query 28 | 29 | self._validate_init_args() 30 | 31 | # Create a list that will hold all of the hashes of the results from 32 | # the query 33 | self.hashes = [] 34 | 35 | self.twitter = twitter.Twitter(auth=twitter.OAuth(self.access_key, 36 | self.access_secret, self.consumer_key, 37 | self.consumer_secret)) 38 | 39 | if self.real: 40 | self.get_randomness(self.query) 41 | else: 42 | self.reseed(self.query) 43 | 44 | def _validate_init_args(self): 45 | """ Make sure all the constructor arguments were passed and are not 46 | None. """ 47 | 48 | birdseed_args = { 49 | 'access_key': self.access_key, 50 | 'access_secret': self.access_secret, 51 | 'consumer_key': self.consumer_key, 52 | 'consumer_secret': self.consumer_secret, 53 | 'query': self.query 54 | } 55 | 56 | # iterate through the keys of the dict 57 | # check that the value it represents is "truthy" (in this case, not 58 | # None) 59 | # if it IS None, raise a ValueError telling the caller it must provide 60 | # that argument 61 | for key in birdseed_args: 62 | if not birdseed_args[key]: 63 | raise ValueError('Please provide `{}`'.format(key)) 64 | 65 | def _real_random(self): 66 | if len(self.hashes) == 0: 67 | self.get_randomness(self.query) 68 | 69 | if len(self.hashes) >= 1: 70 | rand_hash = self.hashes.pop() 71 | # Format hash as a floating point number < 1, just as Python's 72 | # pseudo random generator does 73 | return (int(rand_hash, 16) >> 171) * 0.0000000000000001 74 | else: 75 | raise ValueError('No results returned from API. Either the keys' 76 | 'are too stressed or the search term has no' 77 | 'results.') 78 | 79 | def _pseudo_random(self): 80 | return random.random() 81 | 82 | def _create_hash(self, result): 83 | """ Compute SHA224 hash based on concatenation of the creation time, 84 | the twitter handle and the text.""" 85 | text = (result["created_at"].encode('utf-8') + 86 | result["user"]["screen_name"].encode('utf-8') + 87 | result["text"].encode('utf-8')) 88 | return hashlib.sha224(text).hexdigest() 89 | 90 | def random(self): 91 | """ Public method to get the random number based on if it's pseudo or 92 | real.""" 93 | if self.real: 94 | return self._real_random() 95 | else: 96 | return self._pseudo_random() 97 | 98 | def get_randomness(self, query): 99 | """ Public method to gather 100 tweets based on a search query and 100 | compute their hashes and store them in an internal list.""" 101 | if not self.real: 102 | raise ValueError('Class instance of Birdseed is being run as a' 103 | 'pseudo random number generator. Create a new' 104 | 'instance that is real.') 105 | 106 | query = self.twitter.search.tweets(q=query, count=100) 107 | self.hashes.extend([self._create_hash(result) for result 108 | in query['statuses']]) 109 | 110 | def reseed(self, query): 111 | """ Public method to seed Python's random number generator with the 112 | first tweet obtained from Twitter's API for a particular query.""" 113 | if self.real: 114 | raise ValueError('Class instance of Birdseed is being run as a' 115 | 'real random number generator. Create a new' 116 | 'instance that is pseudo.') 117 | 118 | self.query = query 119 | 120 | # Perform a basic search https://dev.twitter.com/docs/api/1/get/search 121 | query = self.twitter.search.tweets(q=query) 122 | 123 | # Seed Python's random number generator with the first status found 124 | result = query["statuses"][0] 125 | seed = int(hashlib.sha224(self._create_hash(result)).hexdigest(), 16) 126 | random.seed(seed) 127 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal=1 3 | 4 | [metadata] 5 | description-file = README.md -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name='birdseed', 5 | 6 | version='0.2.4', 7 | 8 | description='Twitter random number seeder/generator', 9 | 10 | url='https://github.com/ryanmcdermott/birdseed', 11 | 12 | author='Ryan McDermott', 13 | author_email='ryan.mcdermott@ryansworks.com', 14 | 15 | license='MIT', 16 | 17 | classifiers=[ 18 | 'Development Status :: 3 - Alpha', 19 | 'Intended Audience :: Developers', 20 | 'Topic :: Utilities', 21 | 'License :: OSI Approved :: MIT License', 22 | 'Programming Language :: Python :: 2', 23 | 'Programming Language :: Python :: 2.6', 24 | 'Programming Language :: Python :: 2.7', 25 | 'Programming Language :: Python :: 3', 26 | 'Programming Language :: Python :: 3.2', 27 | 'Programming Language :: Python :: 3.3', 28 | 'Programming Language :: Python :: 3.4', 29 | ], 30 | 31 | keywords='random sysadmin development', 32 | 33 | py_modules=['birdseed'], 34 | 35 | install_requires=['twitter>=1.17.1'], 36 | ) 37 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanmcdermott/birdseed/88a56224a7f831eae509e6944643ba14d3aa77cb/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_birdseed.py: -------------------------------------------------------------------------------- 1 | # We need some tests! 2 | def test_birdseed(): 3 | assert True 4 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py{26,27,33,34} 3 | 4 | [testenv] 5 | basepython = 6 | py26: python2.6 7 | py27: python2.7 8 | py33: python3.3 9 | py34: python3.4 10 | deps = 11 | check-manifest 12 | {py27,py33,py34}: readme 13 | flake8 14 | pytest 15 | commands = 16 | check-manifest --ignore tox.ini,tests* 17 | {py27,py33,py34}: python setup.py check -m -r -s 18 | flake8 . 19 | py.test tests 20 | [flake8] 21 | exclude = .tox,*.egg,build,data 22 | select = E,W,F --------------------------------------------------------------------------------