-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfiggy.py
282 lines (244 loc) · 9.97 KB
/
figgy.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# Fetch video, create gif, upload to gfycat, post to twitter
import yubtub
from yubtub import VideoAccessForbiddenException
import gfycat
import tweepy
import tweet_parser
import time
import logging
import sys
import os, errno
import database
####################
# Making Directories
####################
# Make logs directory
try:
os.mkdir('Logs')
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir('Logs'):
pass
# Make videos directory
try:
os.mkdir('Videos')
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir('Videos'):
pass
# Make gifs directory
try:
os.mkdir('gifs')
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir('gifs'):
pass
####################
# Configuring Logger
####################
# Create new log file for run
current_date = time.strftime('%c')
log_file = 'Logs/Youtube.Jiffer'+current_date+".log"
logging.basicConfig(
filename=log_file,
level=logging.DEBUG,
format='%(asctime)s %(name)-12s: %(levelname)-8s %(message)s')
# Set logging level to DEBUG
logger = logging.getLogger('')
logger.setLevel(logging.DEBUG)
# # Create stream handler to send INFO messages to console
# ch = logging.StreamHandler(sys.stdout)
# ch.setFormatter(logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s'))
# ch.setLevel(logging.INFO)
# # Add console handler to root logger
# logger.addHandler(ch)
#####################
# Configuring Twitter
#####################
# Grab authentication stuff from env
consumer_token = os.environ.get("CONSUMER_TOKEN")
consumer_secret = os.environ.get("CONSUMER_SECRET")
access_key = os.environ.get("ACCESS_KEY")
access_secret = os.environ.get("ACCESS_SECRET")
if consumer_token is None or consumer_secret is None\
or access_key is None or access_secret is None:
# If the environment doesn't have that info, grab it from the keys file
from keys import keys
if keys is not None:
consumer_token = keys["consumer_token"]
consumer_secret = keys["consumer_secret"]
access_key = keys["access_key"]
access_secret = keys["access_secret"]
# If the keys file doesn't have all that info, throw exception
if consumer_token is None or consumer_secret is None\
or access_key is None or access_secret is None:
raise Exception("No authentication information found")
auth = tweepy.OAuthHandler(consumer_token, consumer_secret)
auth.set_access_token(access_key, access_secret)
api = tweepy.API(auth)
######################
### CLI Attributes ###
######################
# If DRY_RUN is True, prevents Figgy from posting to Twitter, writing to DB
DRY_RUN = False
class Figgy(object):
'''
Figgy creates gifs with random lengths from YouTube videos. Videos
are sourced from a connected Twitter account; they are collected
through tweets directed at the account that contain a YouTube URL.
'''
def __init__(self):
self.twitter_api_setup()
def twitter_api_setup(self):
consumer_token = os.environ.get("CONSUMER_TOKEN")
consumer_secret = os.environ.get("CONSUMER_SECRET")
access_key = os.environ.get("ACCESS_KEY")
access_secret = os.environ.get("ACCESS_SECRET")
if consumer_token is None or consumer_secret is None\
or access_key is None or access_secret is None:
# If the environment doesn't have that info, grab it from the keys file
from keys import keys
if keys is not None:
consumer_token = keys["consumer_token"]
consumer_secret = keys["consumer_secret"]
access_key = keys["access_key"]
access_secret = keys["access_secret"]
# If the keys file doesn't have all that info, throw exception
if consumer_token is None or consumer_secret is None\
or access_key is None or access_secret is None:
raise Exception("No authentication information found")
auth = tweepy.OAuthHandler(consumer_token, consumer_secret)
auth.set_access_token(access_key, access_secret)
self.api = tweepy.API(auth)
def upload_gif(self, youtube_url):
'''
Creates a gif of a randomly selected section of the given video
:youtube_url: URL of the youtube video to create a gif from
:return: URL to the gfycat page for the gif
'''
try:
yubtub = yubtub.YubTub(youtube_url)
gif_filename = yubtub.generate_gif()
except Exception as e:
logging.exception(e)
raise e
else:
return self.gfycat_upload(gif_filename)
def gfycat_upload(self, gif_filename):
'''
Uploads the GIF at the given filename to gfycat
:return: URL to the gfycat page for the gif
'''
try:
logging.debug("Uploading gif to gfycat")
gfy_result = gfycat.gfycat().uploadFile(gif_filename)
except Exception as e:
logging.error("Could not upload gif to gfycat")
logging.exception(e)
else:
gfy_url = "http://gfycat.com/" + gfy_result.get("gfyName")
logging.info("Uploaded gif: %s" % gfy_result)
return gfy_url
def tweet_gif(self, youtube_url, tweet=None):
'''
Tweets a randomly created gif from a youtube video.
:youtube_url: URL of the youtube video to gif
:tweet: The tweet that the gif is in response to
:return: The tweet generated with the gif
'''
logging.debug("Uploading gif from youtube: %s" % youtube_url)
url = self.upload_gif(youtube_url);
logging.info("Gif created, posting to twitter")
post_tweets = True
if hasattr(self, "dry_run"):
post_tweets = not getattr(self, "dry_run")
try:
if tweet is not None:
# Respond to given tweet
tweet_text = "@%s %s" % (tweet.user.screen_name, str(url))
if post_tweets:
tweet = self.api.update_status(
status=tweet_text,
in_reply_to_status_id=tweet.id
)
else:
# Just post the thing
if post_tweets:
tweet = self.api.update_status(status=str(url))
# Return the tweet for logging, etc.
return tweet
except Exception as e:
logging.error("Error updating twitter status")
logging.exception(e)
else:
logging.debug("Posted URL for youtube video %s" % youtube_url)
logging.debug(tweet)
def respond_to_mentions(self):
'''
Searches through mentions, finds mentions with youtube links,
responds to those tweets with a random gif from the youtube video.
'''
parser = tweet_parser.TweetParser()
mentions = parser.find_mentions_with_youtubes(api)
if len(mentions) > 0:
# We are in business!
logging.info("Found %i youtube tweets" % len(mentions))
# Check against database to find the hot fresh content
db = database.Database()
only_tweets = [i[0] for i in mentions]
hot_new_tweets = db.find_hot_new_tweets(only_tweets)
logging.info("%i new tweets" % len(hot_new_tweets))
for (tweet, youtube_url) in hot_new_tweets:
logging.debug("New mention: <%s, %i>" % (tweet.text, tweet.id))
try:
new_tweet = self.tweet_gif(youtube_url, tweet)
except VideoAccessForbiddenException as e:
logging.error("Download failed, Forbidden")
db.mark_youtube_invalid(youtube_url)
except Exception as e:
logging.error("Failed to respond to tweet! (<%s, %i>)" \
% (tweet.text, tweet.id))
logging.exception(e)
logging.debug("continuing with responding to tweets")
else:
logging.info("Responded to tweet: <%s, %i> with new \
tweet <%s, %i>" % (tweet.text, tweet.id,\
new_tweet.text, new_tweet.id))
# Register tweet in database
logging.debug("Registering tweet: <%s, %i>" \
% (tweet.text, tweet.id))
db.register_tweet(tweet, new_tweet)
else:
# No tweets :(
logging.info("No youtube mentions found")
if __name__ == "__main__":
# When run from the CLI, this module will search for new mentions
# and respond to them with gifs
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--dry-run", help="performs tasks but without posting to twitter", action="store_true")
parser.add_argument("-v", "--verbosity", help="Increase logging verbosity",
action="store_true")
parser.add_argument("-y", "--youtube", help="YouTube URL to fig")
args = parser.parse_args()
if args.verbosity:
# Create stream handler to send INFO messages to console
ch = logging.StreamHandler(sys.stdout)
ch.setFormatter(logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s'))
ch.setLevel(logging.DEBUG)
# Add console handler to root logger
logger.addHandler(ch)
else:
# Create stream handler to send INFO messages to console
ch = logging.StreamHandler(sys.stdout)
ch.setFormatter(logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s'))
ch.setLevel(logging.INFO)
# Add console handler to root logger
logger.addHandler(ch)
fig = Figgy()
if args.dry_run:
setattr(Figgy, "dry_run", True)
if args.youtube:
# If youtube is provided, just test youtube
logging.info("Testing with given youtube %s" % args.youtube)
fig.tweet_gif(args.youtube)
else:
logging.info("Starting process to search for and respond to mentions")
fig.respond_to_mentions()