Refactoring Telegram bot#

This commit is contained in:
Klaus-Uwe Mitterer 2017-03-22 01:53:51 +01:00
parent ef958366b9
commit de2549ae07
3 changed files with 268 additions and 250 deletions

220
bottools/methods.py Normal file
View file

@ -0,0 +1,220 @@
import ast, dbtools, html, io, logging, moviepy.editor, PIL.Image, random, setuptools, string, bottools.strings, telegram.ext, twitools, urllib.request, tweepy
def getTwo(message):
try:
return twitools.twoBotHelper(message.chat_id)
except ValueError:
message.reply_text(bottools.strings.noauth)
except tweepy.error.TweepError as e:
raise
def twoExceptions(e, message):
text = {
32: bottools.strings.badToken,
36: bottools.strings.selfSpam,
64: bottools.strings.accountSuspended,
88: bottools.strings.rateLimit,
89: bottools.strings.badToken,
99: bottools.strings.badToken,
130: bottools.strings.overload,
131: bottools.strings.twitterError,
161: bottools.strings.followLimit,
185: bottools.strings.tweetLimit,
186: bottools.strings.longTweet,
187: bottools.strings.dupTweet,
205: bottools.strings.rateLimit,
226: bottools.strings.automatedTweet,
271: bottools.strings.selfMute,
272: bottools.strings.notMuted,
323: bottools.strings.multipleGIFs,
326: bottools.strings.accountLocked,
354: bottools.strings.longTweet
}.get(e.api_code)
message.reply_text(text or bottools.strings.twoFail)
# Actual methods:
# ---------------
def start(bot, update):
update.message.reply_text(bottools.strings.start % (setuptools.botname(), setuptools.botname()))
def fish(bot, update):
dbtools.dbHelper().addFish(update.message.chat_id)
update.message.reply_text(bottools.strings.fishThanks)
def getTweetID(tlid, cid):
try:
db = dbtools.dbHelper()
db.executeQuery("SELECT tid FROM timelines WHERE nr = %i AND cid = %i;" % (int(tlid), int(cid)))
return db.getNext()[0]
except Exception:
raise ValueError("No such tweet in timeline")
def toggleTweet(bot, update):
try:
update.message.reply_text(bottools.strings.toggleTweet % (bottools.strings.toggleTweetOn if dbtools.dbHelper().toggleTweet(update.message.chat_id) else bottools.strings.toggleTweetOff))
except tweepy.error.TweepError as e:
bottools.methods.twoExceptions(e, update.message)
def unknown(bot, update):
update.message.reply_text(bottools.strings.unknownCommand)
# Authentication process
def auth(bot, update):
db = dbtools.dbHelper()
cid = update.message.chat_id
if not (db.ato(cid) or db.ase(cid)):
auth = tweepy.OAuthHandler(setuptools.cke(), setuptools.cse())
update.message.reply_text(bottools.strings.auth % auth.get_authorization_url())
dbtools.dbHelper().storeToken(cid, auth.request_token)
else:
update.message.reply_text(bottools.strings.authimp)
def verify(bot, update, args):
db = dbtools.dbHelper()
cid = update.message.chat_id
if db.ato(cid) and not db.ase(cid):
auth = tweepy.OAuthHandler(setuptools.cke(), setuptools.cse())
auth.request_token = ast.literal_eval(db.ato(cid))
try:
auth.get_access_token(args[0])
dbtools.dbHelper().storeUser(cid, auth.access_token, auth.access_token_secret)
update.message.reply_text(bottools.strings.verify)
except Exception as e:
dbtools.dbHelper().deleteUser(update.message.chat_id)
update.message.reply_text(bottools.strings.verifyfail)
else:
update.message.reply_text(bottools.strings.verifyimp)
def unauth(bot, update):
dbtools.dbHelper().deleteUser(update.message.chat_id)
update.message.reply_text(bottools.strings.unauth % setuptools.url())
# User methods
def follow(bot, update, args):
try:
two = bottools.methods.getTwo(update.message)
for user in args:
two.api.create_friendship(screen_name = user)
except tweepy.error.TweepError as e:
bottools.methods.twoExceptions(e, update.message)
def unfollow(bot, update, args):
try:
two = bottools.methods.getTwo(update.message)
for user in args:
two.api.destroy_friendship(screen_name = user)
except tweepy.error.TweepError as e:
bottools.methods.twoExceptions(e, update.message)
# Tweet methods
def explicitTweet(bot, update, args, reply = None):
try:
two = bottools.methods.getTwo(update.message)
if update.message.photo or update.message.document or update.message.video or update.message.sticker:
fid = update.message.document.file_id if update.message.document else update.message.sticker.file_id if update.message.sticker else update.message.video.file_id if update.message.video else update.message.photo[-1].file_id
path = bot.getFile(fid).file_path
media = urllib.request.urlopen(path)
mobj = io.BytesIO(media.read())
filename = path.split("/")[-1]
if filename.split(".")[-1].lower() == "webp":
out = io.BytesIO()
PIL.Image.open(mobj).convert('RGB').save(out, format="JPEG")
filename = "%s.jpg" % filename.split(".")[0]
if update.message.document and filename.split(".")[-1].lower() == "mp4":
temp = ''.join(random.choice(string.ascii_letters + string.digits) for i in range(32))
with open("tmp/%s.%s" % (temp, filename.split(".")[-1]), "wb") as f:
f.write(mobj.getvalue())
moviepy.editor.VideoFileClip("tmp/%s.%s" % (temp, filename.split(".")[-1])).resize(0.3).write_gif("tmp/%s.gif" % temp)
filename = "%s.gif" % temp
out = open("tmp/%s.gif" % temp, "rb")
else:
out = mobj
two.api.update_with_media(filename, update.message.caption, reply, file=out)
out.close()
else:
two.tweet(' '.join(args), reply)
except tweepy.error.TweepError as e:
bottools.methods.twoExceptions(e, update.message)
def reply(bot, update, args):
try:
reply = bottools.methods.getTweetID(args[0], update.message.chat_id)
except:
update.message.reply_text(bottools.strings.cantfind % args[0])
bottools.methods.explicitTweet(bot, update, args[1:], reply)
def retweet(bot, update, args):
for tweet in args:
try:
tid = bottools.methods.getTweetID(tweet, update.message.chat_id)
bottools.methods.getTwo(update.message).api.retweet(tid)
except ValueError:
update.message.reply_text(bottools.strings.cantfind % tweet)
except tweepy.error.TweepError as e:
bottools.methods.twoExceptions(e, update.message)
def like(bot, update, args):
for tweet in args:
try:
tid = bottools.methods.getTweetID(tweet, update.message.chat_id)
bottools.methods.getTwo(update.message).api.create_favorite(tid)
except ValueError:
update.message.reply_text(bottools.strings.cantfind % tweet)
except tweepy.error.TweepError as e:
bottools.methods.twoExceptions(e, update.message)
def tweet(bot, update):
try:
if dbtools.dbHelper().getTStatus(update.message.chat_id):
bottools.methods.explicitTweet(bot, update, [update.message.text])
except twepy.error.TweepError as e:
bottools.methods.twoExceptions(e, update.message)
# Timelines
def timeline(bot, update, args = [10]):
try:
count = int(args[0])
except:
count = 10
db = dbtools.dbHelper()
db.executeQuery("DELETE FROM timelines WHERE cid = %i;" % int(update.message.chat_id))
try:
i = 1
two = bottools.methods.getTwo(update.message)
for status in two.api.home_timeline(count=count):
db.executeQuery("INSERT INTO timelines VALUES(%i, %i, %i);" % (update.message.chat_id, i, status.id))
update.message.reply_text("Tweet %i:\n%s (@%s) at %s:\n%s" % (i, status.author.name, status.author.screen_name, status.created_at, html.unescape(status.text)))
i += 1
except tweepy.error.TweepError as e:
bottools.methods.twoExceptions(e, update.message)
db.commit()

View file

@ -7,12 +7,12 @@ You will receive a six-digit PIN. Please send it to me like this:
* /verify 123456''' * /verify 123456'''
authimp = '''I can't currently start a new authentication process for you as you are either already authenticated or an authentication process has been started. authimp = '''You can't currently log in to a new account as you are either already logged in or I'm waiting for your verification code.
Please unauthenticate using /unauth first if you are sure you want to re-authenticate.''' If you wish to log in to a different account or start the login process from the top, please /logout first..'''
noauth = '''You are not authenticated. Please use /auth to sign in with Twitter.''' noauth = '''You are not logged in. Please use /login to sign in with Twitter.'''
start = '''Hey there! start = '''Hey there!
@ -21,21 +21,29 @@ I'm @%s, everybody's favorite Twitter bot on Telegram!
For me to help you, you will first have to authenticate with Twitter: For me to help you, you will first have to authenticate with Twitter:
* /auth * /login
After authentication, you will be able to tweet from your account. Just drop me a note! After authentication, you will be able to tweet from your account. Just drop me a note!
Additionally, you will be able to use the following commands: Additionally, you will be able to use the following commands:
* /follow USER - Follow a user. * User functions:
* /like TWEET - Like a tweet. You will need to pass a tweet ID (TWEET) as returned by /timeline. ** /follow USER - Follow a user.
* /reply TWEET TEXT - Reply TEXT to a tweet. You will need to pass a tweet ID (TWEET) as returned by /timeline. ** /unfollow USER - Unfollow a user.
* /retweet TWEET - Retweet a tweet. You will need to pass a tweet ID (TWEET) as returned by /timeline.
* /timeline - Shows you the latest tweets in your timeline. Returns 10 tweets by default, but you may also use it like /timeline 20, which would return 20 tweets. * Tweet functions:
* /toggletweet - Turn automatic tweeting of messages on/off. Useful in groups. ** /like TWEET - Like a tweet. You will need to pass a tweet ID (TWEET) as returned by /timeline.
* /tweet TEXT - Explicitly tweet TEXT even if automatic tweeting is off (/toggletweet). ** /reply TWEET TEXT - Reply TEXT to a tweet. You will need to pass a tweet ID (TWEET) as returned by /timeline.
* /unauth - Revoke your authenticaton and stop using %s. ** /retweet TWEET - Retweet a tweet. You will need to pass a tweet ID (TWEET) as returned by /timeline.
* /unfollow USER - Unfollow a user. ** /toggletweet - Turn automatic tweeting of messages on/off. Useful in groups.
** /tweet TEXT - Explicitly tweet TEXT even if automatic tweeting is off (/toggletweet).
* Timeline functions:
** /timeline - Shows you the latest tweets in your timeline. Returns 10 tweets by default, but you may also use it like /timeline 20, which would return 20 tweets.
If you have any further questions about these commands, I'll try to help you. Just use /help COMMAND for that. For instance, "/help timeline" will show you further details about the /timeline command.
In case you ever wish to leave, you can use /logout. Of course, you're always welcome to login again after that.
Have fun!''' Have fun!'''
@ -61,13 +69,13 @@ verify = '''Thanks for authenticating. You can now use all of my features!'''
verifyfail = '''Oops, something went wrong during the authentication. Please try again: verifyfail = '''Oops, something went wrong during the authentication. Please try again:
* /auth''' * /login'''
verifyimp = '''There is not currently an authentication process running for you. You may already be logged in, or you have not yet sent me an /auth command.''' verifyimp = '''Huh. I wasn't expecting a verification code from you. You may already be logged in, or you have not yet sent me a /login command.'''
twoFail = '''Well, something went wrong. Try again later.''' twoFail = '''Well, something went wrong. Try again later.'''
twoAuthFail = '''Could not authenticate you for some reason. Please try again later. If this problem persists, please try reauthenticating using /unauth and /auth.''' twoAuthFail = '''Could not authenticate you for some reason. Please try again later. If this problem persists, please try reauthenticating using /logout and /login.'''
toggleTweetOn = "on" toggleTweetOn = "on"
@ -81,7 +89,7 @@ accountSuspended = '''I'm afraid your account is suspended. I can't currently he
rateLimit = '''Whoa, buddy! Slow it down! You ran into a rate limit. Please try again later.''' rateLimit = '''Whoa, buddy! Slow it down! You ran into a rate limit. Please try again later.'''
badToken = '''Oops. Something appears to be wrong with your credentials. Please try reauthenticating using /unauth and /auth.''' badToken = '''Oops. Something appears to be wrong with your credentials. Please try reauthenticating using /logout and /login.'''
overload = '''So, I've good good news and bad news. The bad news is, I was unable to process your request. The good news is, it isn't our fault. Seems like Twitter is experiencing higher load than usual. Please try again later.''' overload = '''So, I've good good news and bad news. The bad news is, I was unable to process your request. The good news is, it isn't our fault. Seems like Twitter is experiencing higher load than usual. Please try again later.'''

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import ast, dbtools, html, io, logging, moviepy.editor, PIL.Image, random, setuptools, string, bottools.strings, telegram.ext, twitools, urllib.request, tweepy import ast, bottools.methods, dbtools, html, io, logging, moviepy.editor, PIL.Image, random, setuptools, string, bottools.strings, telegram.ext, twitools, urllib.request, tweepy
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO) logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -8,242 +8,32 @@ logger = logging.getLogger(__name__)
def log(bot, update, error): def log(bot, update, error):
logger.warn("Error %s caused by '%s'" % (error, update)) logger.warn("Error %s caused by '%s'" % (error, update))
def twoExceptions(e, message):
text = {
32: bottools.strings.badToken,
36: bottools.strings.selfSpam,
64: bottools.strings.accountSuspended,
88: bottools.strings.rateLimit,
89: bottools.strings.badToken,
99: bottools.strings.badToken,
130: bottools.strings.overload,
131: bottools.strings.twitterError,
161: bottools.strings.followLimit,
185: bottools.strings.tweetLimit,
186: bottools.strings.tweetLimit,
187: bottools.strings.dupTweet,
205: bottools.strings.rateLimit,
226: bottools.strings.automatedTweet,
271: bottools.strings.selfMute,
272: bottools.strings.notMuted,
323: bottools.strings.multipleGIFs,
326: bottools.strings.accountLocked,
354: bottools.strings.longTweet
}.get(e.api_code)
message.reply_text(text or bottools.strings.twoFail)
def getTwo(message):
try:
return twitools.twoBotHelper(message.chat_id)
except ValueError:
message.reply_text(bottools.strings.noauth)
except tweepy.error.TweepError as e:
raise
def start(bot, update):
update.message.reply_text(bottools.strings.start % (setuptools.botname(), setuptools.botname()))
def auth(bot, update):
db = dbtools.dbHelper()
cid = update.message.chat_id
if not (db.ato(cid) or db.ase(cid)):
auth = tweepy.OAuthHandler(setuptools.cke(), setuptools.cse())
update.message.reply_text(bottools.strings.auth % auth.get_authorization_url())
dbtools.dbHelper().storeToken(cid, auth.request_token)
else:
update.message.reply_text(bottools.strings.authimp)
def verify(bot, update, args):
db = dbtools.dbHelper()
cid = update.message.chat_id
if db.ato(cid) and not db.ase(cid):
auth = tweepy.OAuthHandler(setuptools.cke(), setuptools.cse())
auth.request_token = ast.literal_eval(db.ato(cid))
try:
auth.get_access_token(args[0])
dbtools.dbHelper().storeUser(cid, auth.access_token, auth.access_token_secret)
update.message.reply_text(bottools.strings.verify)
except Exception as e:
dbtools.dbHelper().deleteUser(update.message.chat_id)
update.message.reply_text(bottools.strings.verifyfail)
else:
update.message.reply_text(bottools.strings.verifyimp)
def unauth(bot, update):
dbtools.dbHelper().deleteUser(update.message.chat_id)
update.message.reply_text(bottools.strings.unauth % setuptools.url())
def fish(bot, update):
dbtools.dbHelper().addFish(update.message.chat_id)
update.message.reply_text(bottools.strings.fishThanks)
def follow(bot, update, args):
try:
two = getTwo(update.message)
for user in args:
two.api.create_friendship(screen_name = user)
except tweepy.error.TweepError as e:
twoExceptions(e, update.message)
def unfollow(bot, update, args):
try:
two = getTwo(update.message)
for user in args:
two.api.destroy_friendship(screen_name = user)
except tweepy.error.TweepError as e:
twoExceptions(e, update.message)
def getTweetID(tlid, cid):
try:
db = dbtools.dbHelper()
db.executeQuery("SELECT tid FROM timelines WHERE nr = %i AND cid = %i;" % (int(tlid), int(cid)))
return db.getNext()[0]
except Exception:
raise ValueError("No such tweet in timeline")
def explicitTweet(bot, update, args, reply = None):
try:
two = getTwo(update.message)
if update.message.photo or update.message.document or update.message.video or update.message.sticker:
fid = update.message.document.file_id if update.message.document else update.message.sticker.file_id if update.message.sticker else update.message.video.file_id if update.message.video else update.message.photo[-1].file_id
path = bot.getFile(fid).file_path
media = urllib.request.urlopen(path)
mobj = io.BytesIO(media.read())
filename = path.split("/")[-1]
if filename.split(".")[-1].lower() == "webp":
out = io.BytesIO()
PIL.Image.open(mobj).convert('RGB').save(out, format="JPEG")
filename = "%s.jpg" % filename.split(".")[0]
if update.message.document and filename.split(".")[-1].lower() == "mp4":
temp = ''.join(random.choice(string.ascii_letters + string.digits) for i in range(32))
with open("tmp/%s.%s" % (temp, filename.split(".")[-1]), "wb") as f:
f.write(mobj.getvalue())
moviepy.editor.VideoFileClip("tmp/%s.%s" % (temp, filename.split(".")[-1])).resize(0.3).write_gif("tmp/%s.gif" % temp)
filename = "%s.gif" % temp
out = open("tmp/%s.gif" % temp, "rb")
else:
out = mobj
two.api.update_with_media(filename, update.message.caption, reply, file=out)
out.close()
else:
two.tweet(' '.join(args), reply)
except tweepy.error.TweepError as e:
twoExceptions(e, update.message)
def reply(bot, update, args):
try:
reply = getTweetID(args[0], update.message.chat_id)
except:
update.message.reply_text(bottools.strings.cantfind % args[0])
explicitTweet(bot, update, args[1:], reply)
def retweet(bot, update, args):
for tweet in args:
try:
tid = getTweetID(tweet, update.message.chat_id)
getTwo(update.message).api.retweet(tid)
except ValueError:
update.message.reply_text(bottools.strings.cantfind % tweet)
except tweepy.error.TweepError as e:
twoExceptions(e, update.message)
def like(bot, update, args):
for tweet in args:
try:
tid = getTweetID(tweet, update.message.chat_id)
getTwo(update.message).api.create_favorite(tid)
except ValueError:
update.message.reply_text(bottools.strings.cantfind % tweet)
except tweepy.error.TweepError as e:
twoExceptions(e, update.message)
def tweet(bot, update):
try:
if dbtools.dbHelper().getTStatus(update.message.chat_id):
explicitTweet(bot, update, [update.message.text])
except twepy.error.TweepError as e:
twoExceptions(e, update.message)
def timeline(bot, update, args = [10]):
try:
count = int(args[0])
except:
count = 10
db = dbtools.dbHelper()
db.executeQuery("DELETE FROM timelines WHERE cid = %i;" % int(update.message.chat_id))
try:
i = 1
two = getTwo(update.message)
for status in two.api.home_timeline(count=count):
db.executeQuery("INSERT INTO timelines VALUES(%i, %i, %i);" % (update.message.chat_id, i, status.id))
update.message.reply_text("Tweet %i:\n%s (@%s) at %s:\n%s" % (i, status.author.name, status.author.screen_name, status.created_at, html.unescape(status.text)))
i += 1
except tweepy.error.TweepError as e:
twoExceptions(e, update.message)
db.commit()
def toggleTweet(bot, update):
try:
update.message.reply_text(bottools.strings.toggleTweet % (bottools.strings.toggleTweetOn if dbtools.dbHelper().toggleTweet(update.message.chat_id) else bottools.strings.toggleTweetOff))
except tweepy.error.TweepError as e:
twoExceptions(e, update.message)
def unknown(bot, update):
update.message.reply_text(bottools.strings.unknownCommand)
def test(bot, update, args):
print(args)
unknown(bot, update)
if __name__ == "__main__": if __name__ == "__main__":
updater = telegram.ext.Updater(token=setuptools.token()) updater = telegram.ext.Updater(token=setuptools.token())
updater.dispatcher.add_handler(telegram.ext.CommandHandler("auth", auth)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("auth", bottools.methods.auth))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("fish", fish)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("fish", bottools.methods.fish))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("follow", follow, pass_args=True)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("follow", bottools.methods.follow, pass_args=True))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("help", start)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("help", bottools.methods.start))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("like", like, pass_args=True)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("like", bottools.methods.like, pass_args=True))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("reply", reply, pass_args=True)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("login", bottools.methods.auth))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("retweet", retweet, pass_args=True)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("logout", bottools.methods.unauth))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("start", start)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("reply", bottools.methods.reply, pass_args=True))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("test", test, pass_args=True)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("retweet", bottools.methods.retweet, pass_args=True))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("timeline", timeline, pass_args=True)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("start", bottools.methods.start))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("toggletweet", toggleTweet)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("timeline", bottools.methods.timeline, pass_args=True))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("tweet", explicitTweet, pass_args=True)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("toggletweet", bottools.methods.toggleTweet))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("unauth", unauth)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("tweet", bottools.methods.explicitTweet, pass_args=True))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("unfollow", unfollow, pass_args=True)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("unauth", bottools.methods.unauth))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("verify", verify, pass_args=True)) updater.dispatcher.add_handler(telegram.ext.CommandHandler("unfollow", bottools.methods.unfollow, pass_args=True))
updater.dispatcher.add_handler(telegram.ext.CommandHandler("verify", bottools.methods.verify, pass_args=True))
updater.dispatcher.add_handler(telegram.ext.MessageHandler(telegram.ext.Filters.text, tweet)) updater.dispatcher.add_handler(telegram.ext.MessageHandler(telegram.ext.Filters.text, bottools.methods.tweet))
updater.dispatcher.add_handler(telegram.ext.MessageHandler(telegram.ext.Filters.photo, tweet)) updater.dispatcher.add_handler(telegram.ext.MessageHandler(telegram.ext.Filters.photo, bottools.methods.tweet))
updater.dispatcher.add_handler(telegram.ext.MessageHandler(telegram.ext.Filters.sticker, tweet)) updater.dispatcher.add_handler(telegram.ext.MessageHandler(telegram.ext.Filters.sticker, bottools.methods.tweet))
updater.dispatcher.add_handler(telegram.ext.MessageHandler(telegram.ext.Filters.video, tweet)) updater.dispatcher.add_handler(telegram.ext.MessageHandler(telegram.ext.Filters.video, bottools.methods.tweet))
updater.dispatcher.add_handler(telegram.ext.MessageHandler(telegram.ext.Filters.command, unknown)) updater.dispatcher.add_handler(telegram.ext.MessageHandler(telegram.ext.Filters.command, bottools.methods.unknown))
updater.dispatcher.add_handler(telegram.ext.MessageHandler(telegram.ext.Filters.document, tweet)) updater.dispatcher.add_handler(telegram.ext.MessageHandler(telegram.ext.Filters.document, bottools.methods.tweet))
updater.dispatcher.add_error_handler(log) updater.dispatcher.add_error_handler(log)