Skip to content

Instantly share code, notes, and snippets.

@TheTimgor
Last active June 3, 2020 00:47
Show Gist options
  • Save TheTimgor/81f54930bf9012a5ba7af4ca4728ab9c to your computer and use it in GitHub Desktop.
Save TheTimgor/81f54930bf9012a5ba7af4ca4728ab9c to your computer and use it in GitHub Desktop.
@moeshitbot annotated source code
# ___ _ ______ ____
# / | | / / __ \/ __ \
# / /| | | /| / / / / / / / /
# / ___ | |/ |/ / /_/ / /_/ /
# /_/ |_|__/|__/\____/\____/
#
# @moeshitbot, by timgor | timgor.moe
# annotated source code
#
# this is my shitty cute anime pics twitter bot.
# it makes requests to my timgor.moe api and yeets them up on to twitter.
# a lot of the heavy lifting is done by tweepy (www.tweepy.org),
# a twitter api wrapper for python. it makes doing this all an absolute breeze
# and is the gold standard for making twitter bots.
#
# also huge thanks to saucenao.com for letting me procedurally get the source (sauce)
# for all the images. i would never make this kind of bot without giving the original
# artists credit for their hard work, and saucenao provides me with that ability.
# for those unaquainted, saucenao is a reverse image search for artwork that
# is a goddamn blessing for figuring out who drew that amzing lewd you just downloaded.
#
print('sending a tweet')
import json
import tweepy
import requests
from io import BytesIO
import urllib
from random import choice
# load the saucenao api creds from a local json file
with open('saucenao_credentials.json') as f:
saucenao_credentials = json.load(f)
# request to saucenao api
# tries with up to 100 different images just in case it can't find the sauce for one
sauce = ''
try_num = 0
num_tries = 100
while not sauce: # try until we hit sauce
url = requests.get('https://timgor.moe/api/random') # get image url from timgor.moe api
# make the request to the saucenao api
# probably should've encoded the query parameters more readably
# basically means:
# json format
# one result
# >80% similarity
# all databases
sn_url = 'http://saucenao.com/search.php?output_type=2&numres=1&minsim=80!&db=999&api_key='+saucenao_credentials['api_key']+'&url='+urllib.parse.quote(url.content)
sn_response = requests.get(sn_url)
# it's json parsing time
sn_json = json.loads(sn_response.content)
result = sn_json['results'][0]
# set sauce if we got anything with high enough similarity
if float(result['header']['similarity']) > 80:
sauce = result['data']['ext_urls'][0]
try_num += 1
# exit if retries exceeded
if num_tries < try_num:
print('retries exceeded')
exit(1)
# download the image and proceed if successfull
data = requests.get(url.content)
if data.status_code == 200:
# this code is designed to be as portable as possible,
# and runnable in places where you don't neccesarilly have
# write access. to have a file object usable with tweepy,
# i create a BytesIO object which acts a file, but is kept only in RAM
image = BytesIO(data.content)
# load twitter api and user creds from a local json file
with open('twitter_credentials.json') as f:
twitter_credentials = json.load(f)
# the witty messages are also kept externally
# it's nice to have these kinds of things in a seperate file so it's harder
# (not impossible) to break the whole program adding one
# this also maximizes code prettyness and reusability
with open('messages.json') as f:
messages = json.load(f)
# messages are all encoded as string.format() templates so i can easily insert the sauce url, like so:
# "programmatically selected from my anime pics folder. \nsauce: {0} (courtesy of saucenao.com)"
message = choice(messages).format(sauce) # choose random message
# tweepy wants a filename with an extension for guessing MIME content output_type
# i need to extract it from the headers of the response i got earlier
# this clusterfuck of a listcomp does that, i don't remember how
filename = [a for a in data.headers['Content-Disposition'].split(';') if 'filename=' in a][0].replace('filename=','').replace('"','')
# tweepy handles all the authorization for you
# manually dealing with oauth2 is like having razor blades that
# have been heated to 1000 C shoved up your dick hole. it's not fun i assure you
# i am very grateful for tweepy
auth = tweepy.OAuthHandler(twitter_credentials['consumer_key'], twitter_credentials['consumer_secret'])
auth.set_access_token(twitter_credentials['access_token'], twitter_credentials['access_token_secret'])
api = tweepy.API(auth)
try:
# sends the tweet with the BytesIO object and filename from earlier
api.update_with_media(filename,status=message,file=image)
except:
# that's right kids, except: pass in actual production code
# please never do this
# do actual error handling
# don't be like me
pass
print('tweet sent') # we did it, reddit!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment