├── .gitignore ├── oauth_dance.py ├── c3daysleft_tweet.py ├── c3daysleft_tweets_default.json ├── c3daysleft_tweets_special.json ├── c3daysleft.py └── c3daysleft_test.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | *~ 4 | -------------------------------------------------------------------------------- /oauth_dance.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import twitter 3 | import bot_apikeys 4 | import sys 5 | 6 | if len(sys.argv) > 1: 7 | tokenfile = sys.argv[1] 8 | else: 9 | tokenfile = None 10 | 11 | twitter.oauth_dance('c3daysleftbot', bot_apikeys.CONSUMER_KEY, bot_apikeys.CONSUMER_SECRET, tokenfile) 12 | -------------------------------------------------------------------------------- /c3daysleft_tweet.py: -------------------------------------------------------------------------------- 1 | __author__ = 'd3non' 2 | 3 | from c3daysleft import sleep, twitter, get_teweet, get_format 4 | from c3daysleft import SLEEP_MAX, SLEEP_MIN, OAUTH_FILE 5 | 6 | import bot_apikeys 7 | 8 | 9 | if __name__ == "__main__": 10 | sleep(SLEEP_MIN, SLEEP_MAX) 11 | oauth = twitter.read_token_file(OAUTH_FILE) 12 | t = twitter.Twitter(auth=twitter.OAuth(oauth[0], oauth[1], bot_apikeys.CONSUMER_KEY, bot_apikeys.CONSUMER_SECRET)) 13 | text = get_teweet().format(**get_format()) 14 | print("tweeting: %s" % text) 15 | t.statuses.update(status=text) -------------------------------------------------------------------------------- /c3daysleft_tweets_default.json: -------------------------------------------------------------------------------- 1 | { 2 | "Goooood Mooooorning, Twietnaaam! On this lovely day, there are only {days_left} days left until #{nextC3}": 5, 3 | "What a nice day! And wow, only {days_left} days remain until #{nextC3} starts! #anticipation": 1, 4 | "I'm very pleased to remind you that #{nextC3} will take place in only {days_left} days!": 2, 5 | "Bugged by all those strange people out there? Chin up, #{nextC3} will start in only {days_left} days!! :)": 2, 6 | "Bugged by all those strange people out there? Chin up, #{nextC3} will start in only 0x{days_left:X} days!! :)": 3, 7 | "Bugged by all those strange people out there? Chin up, #{nextC3} will start in only b{days_left:b} days!! :)": 1, 8 | "{days_left} days remaining!! Wooohoooo :D #{nextC3}": 1, 9 | "Only b{days_left:b} days left until #{nextC3}": 2, 10 | "Only 0x{days_left:X} days left until #{nextC3}": 2, 11 | "Only {days_left} days left until #{nextC3}": 20 12 | } -------------------------------------------------------------------------------- /c3daysleft_tweets_special.json: -------------------------------------------------------------------------------- 1 | { 2 | "2014-01-05": "So, the first Sunday after #30C3. Is your sleep cycle still as out of whack as mine? We have 356 days to recover until #31C3 starts :D", 3 | "2014-01-13": "Day 4 of #30C3 was two weeks ago! That leaves only 355 days until #31C3 \\o/", 4 | "2014-01-20": "Oh yea, only b'101010101 days left until #31C3", 5 | "2014-07-31": "With {days_left} days left until #31C3, we are running a new version of this bot :>", 6 | "2014-08-01": "Ohai August, the #31C3 will be in {days_left} days! :D", 7 | "2014-08-02": "While waiting the 148 days left until #31C3, you could take a look at this bot and delight others with awesome tweets http://git.io/FWvIag", 8 | "2014-11-25": "I'm happy to tell you, ticket order for #31C3 is open! https://tickets.events.ccc.de/ And you only have to wait {days_left} more days until its beginning!", 9 | "2014-12-29": "Already two days over on #31C3 and two more awesome days to come! <3", 10 | "2014-12-30": "Wish you all another awesome (and sadly last) day on #31C3", 11 | "2014-12-31": "Whaaat, #31C3 over already?! :(\nHave a nice way back 'home', only little conference flue and see you in {days_left} days on #32C3!", 12 | "2015-11-18": "Oohh, only {days_left} tickets^w days left!! Order your tickets soon https://tickets.events.ccc.de/ ;) #32C3!", 13 | "2015-11-29": "4 weeks! Only four weeks left until #32C3 begins!!", 14 | "2015-12-28": "I wish you all a great time and three more days at #32C3", 15 | "2015-12-29": "Half time at #32C3 - two more great days and nights to go!", 16 | "2015-12-30": "Oh noez, already the last day of #32C3, have a lot of fun and an awesome afterparty as usual! :)", 17 | "2015-12-31": "Hope you survived the #32C3 and the afterparty well, have a nice way back hope and see you in {days_left} days at #33C3", 18 | "2016-04-16": "Yeeaaa, only b{days_left:b} days left until #{nextC3}!" 19 | } 20 | -------------------------------------------------------------------------------- /c3daysleft.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | import twitter 3 | import bot_apikeys 4 | import datetime 5 | import random 6 | import json 7 | 8 | TWEETS_DEFAULT_FILE = "c3daysleft_tweets_default.json" 9 | TWEETS_SPECIAL_FILE = "c3daysleft_tweets_special.json" 10 | 11 | OAUTH_FILE = "account.oauth" 12 | 13 | SLEEP_MIN = 60*30 14 | SLEEP_MAX = 3600*23 15 | 16 | 17 | def get_nextC3date(date=datetime.date.today()): 18 | if date.month == 12 and date.day > 30: # YYYY-12-30 will stay the last day of C3? ;) 19 | nextC3date = datetime.date(date.year + 1, 12, 27) 20 | else: 21 | nextC3date = datetime.date(date.year, 12, 27) 22 | return nextC3date 23 | 24 | 25 | def weighted_random(pairs): 26 | total = sum(pair[0] for pair in pairs) 27 | r = random.randint(1, total) 28 | for (weight, value) in pairs: 29 | r -= weight 30 | if r <= 0: 31 | return value 32 | 33 | 34 | def get_teweet(date=datetime.date.today(), special_file=TWEETS_SPECIAL_FILE, 35 | default_file=TWEETS_DEFAULT_FILE): 36 | s = str(date.today()) 37 | special = json.load(open(special_file, 'r')) 38 | if s in special: 39 | return special[s] 40 | normal = json.load(open(default_file, 'r')) 41 | normal_pairs = [(normal[x], x) for x in normal] 42 | return weighted_random(normal_pairs) 43 | 44 | 45 | def get_format(date=datetime.date.today(), nextC3date=None): 46 | if nextC3date is None: 47 | nextC3date = get_nextC3date(date) 48 | return { 49 | 'days_left': (nextC3date - date).days, 50 | 'nextC3_date': nextC3date, 51 | 'nextC3': "%sC3" % (nextC3date.year - 1983) 52 | } 53 | 54 | 55 | def sleep(sleep_min, sleep_max=None): 56 | if sleep_max is None: 57 | sleep_max = sleep_min 58 | if sleep_min and sleep_max and (sleep_max >= sleep_min): 59 | from time import sleep 60 | sleeptime = random.randint(sleep_min, sleep_max) 61 | print("sleeping for %ss" % sleeptime) 62 | sleep(sleeptime) -------------------------------------------------------------------------------- /c3daysleft_test.py: -------------------------------------------------------------------------------- 1 | __author__ = 'd3non' 2 | 3 | import unittest 4 | import datetime 5 | 6 | from c3daysleft import sleep, twitter, get_teweet, get_format, get_nextC3date 7 | 8 | 9 | class c3daysleftTests(unittest.TestCase): 10 | 11 | def test_get_nextC3date(self): 12 | self.assertEqual(get_nextC3date(datetime.date(2014, 12, 1)), datetime.date(2014, 12, 27)) 13 | self.assertEqual(get_nextC3date(datetime.date(2014, 12, 27)), datetime.date(2014, 12, 27)) 14 | self.assertEqual(get_nextC3date(datetime.date(2014, 12, 29)), datetime.date(2014, 12, 27)) 15 | self.assertEqual(get_nextC3date(datetime.date(2014, 12, 31)), datetime.date(2015, 12, 27)) 16 | self.assertEqual(get_nextC3date(datetime.date(2015, 6, 1)), datetime.date(2015, 12, 27)) 17 | 18 | def test_get_format(self): 19 | date = datetime.date(2014, 8, 1) 20 | format_map = get_format(nextC3date=get_nextC3date(date), date=date) 21 | self.assertEqual(format_map, 22 | {'days_left': 148, 'nextC3': '31C3', 'nextC3_date': datetime.date(2014, 12, 27)}) 23 | 24 | def test_get_format_day1(self): 25 | date = datetime.date(2014, 12, 27) 26 | format_map = get_format(nextC3date=get_nextC3date(date), date=date) 27 | self.assertEqual(format_map, 28 | {'days_left': 0, 'nextC3': '31C3', 'nextC3_date': datetime.date(2014, 12, 27)}) 29 | 30 | def test_get_format_day3(self): 31 | date = datetime.date(2014, 12, 29) 32 | format_map = get_format(nextC3date=get_nextC3date(date), date=date) 33 | self.assertEqual(format_map, 34 | {'days_left': -2, 'nextC3': '31C3', 'nextC3_date': datetime.date(2014, 12, 27)}) 35 | 36 | def test_get_format_day5(self): 37 | date = datetime.date(2014, 12, 31) 38 | format_map = get_format(nextC3date=get_nextC3date(date), date=date) 39 | self.assertEqual(format_map, 40 | {'days_left': 361, 'nextC3': '32C3', 'nextC3_date': datetime.date(2015, 12, 27)}) 41 | 42 | def test_sleep(self): 43 | start = datetime.datetime.now() 44 | sleep(1, 5) 45 | slept = (datetime.datetime.now() - start).seconds 46 | self.assertTrue(slept >= 1) 47 | self.assertTrue(slept <= 5) 48 | 49 | def test_sleep_exact(self): 50 | start = datetime.datetime.now() 51 | sleep(3, 3) 52 | self.assertEqual(3, (datetime.datetime.now() - start).seconds) 53 | 54 | if __name__ == "__main__": 55 | c3daysleftTests().run() --------------------------------------------------------------------------------