diff --git a/bot.py b/bot.py new file mode 100755 index 0000000..304e2ca --- /dev/null +++ b/bot.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 + +import ast, dbtools, logging, setuptools, strings, telegram.ext, twitools, tweepy + +logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO) +logger = logging.getLogger(__name__) + +def log(bot, update, error): + logger.warn("Error %s caused by '%s'" % (error, update)) + +updater = telegram.ext.Updater(token=setuptools.token()) + +def noauth(update): + update.message.reply_text(strings.noauth) + +def start(bot, update): + update.message.reply_text(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(strings.auth % auth.get_authorization_url()) + dbtools.dbHelper().storeToken(cid, auth.request_token) + + else: + update.message.reply_text(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(strings.verify) + + except Exception as e: + dbtools.dbHelper().deleteUser(update.message.chat_id) + update.message.reply_text(strings.verifyfail) + + else: + update.message.reply_text(strings.verifyimp) + +def unauth(bot, update): + dbtools.dbHelper().deleteUser(update.message.chat_id) + update.message.reply_text(strings.unauth % setuptools.url()) + +def fish(bot, update): + dbtools.dbHelper().addFish(update.message.chat_id) + update.message.reply_text("Yummy! Thanks! :3") + +def explicitTweet(bot, update, args): + two = twitools.twoBotHelper(update.message.chat_id) + two.tweet(' '.join(args)) + +def tweet(bot, update): + try: + if dbtools.dbHelper().getTStatus(update.message.chat_id): + explicitTweet(bot, update, [update.message.text]) + except: + noauth(update) + +def timeline(bot, update, args = [10]): + try: + count = int(args[0]) + except: + count = 10 + + two = twitools.twoBotHelper(update.message.chat_id) + + for status in two.api.home_timeline(count=count): + update.message.reply_text("%s (%s) at %s: %s" % (status.author.name, status.author.screen_name, status.created_at, status.text)) + +def toggleTweet(bot, update): + try: + update.message.reply_text(strings.toggleTweet % ("on" if dbtools.dbHelper().toggleTweet(update.message.chat_id) else "off")) + except: + noauth(update) + +def unknown(bot, update): + update.message.reply_text("Sorry, I didn't understand that command.") + +def test(bot, update, args): + print(args) + unknown(bot, update) + +updater.dispatcher.add_handler(telegram.ext.CommandHandler("auth", auth)) +updater.dispatcher.add_handler(telegram.ext.CommandHandler("fish", fish)) +updater.dispatcher.add_handler(telegram.ext.CommandHandler("help", start)) +updater.dispatcher.add_handler(telegram.ext.CommandHandler("start", start)) +updater.dispatcher.add_handler(telegram.ext.CommandHandler("test", test, pass_args=True)) +updater.dispatcher.add_handler(telegram.ext.CommandHandler("timeline", timeline)) +updater.dispatcher.add_handler(telegram.ext.CommandHandler("toggletweet", toggleTweet)) +updater.dispatcher.add_handler(telegram.ext.CommandHandler("tweet", explicitTweet, pass_args=True)) +updater.dispatcher.add_handler(telegram.ext.CommandHandler("unauth", unauth)) +updater.dispatcher.add_handler(telegram.ext.CommandHandler("verify", 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.command, unknown)) + +updater.dispatcher.add_error_handler(log) + +updater.start_polling() +updater.idle() diff --git a/dbtools/__init__.py b/dbtools/__init__.py index 95d508e..ee4508d 100644 --- a/dbtools/__init__.py +++ b/dbtools/__init__.py @@ -108,6 +108,52 @@ class dbObject: pass return False + def deleteUser(self, cid): + self.executeQuery("DELETE FROM tokens WHERE cid = %i;" % int(cid)) + self.commit() + + def storeUser(self, cid, ato, ase): + self.executeQuery("DELETE FROM tokens WHERE cid = %i;" % int(cid)) + self.executeQuery("INSERT INTO tokens(cid, ato, ase) VALUES(%i, '%s', '%s');" % (int(cid), ato, ase)) + self.commit() + + def ato(self, cid): + try: + self.executeQuery("SELECT ato FROM tokens WHERE cid = %i;" % int(cid)) + return self.cur.fetchone()[0] + except: + return False + + def ase(self, cid): + try: + self.executeQuery("SELECT ase FROM tokens WHERE cid = %i;" % int(cid)) + return self.cur.fetchone()[0] + except: + return False + + def getTStatus(self, cid): + try: + self.executeQuery("SELECT tweet FROM tokens WHERE cid = %i;" % int(cid)) + + return True if int(self.cur.fetchone()[0]) == 1 else False + + except: + raise ValueError("No such user: %i" % int(cid)) + + def toggleTweet(self, cid): + self.executeQuery("UPDATE tokens SET tweet = NOT tweet WHERE cid = %i;" % int(cid)) + self.commit() + + return self.getTStatus(cid) + + def addFish(self, cid): + self.executeQuery("UPDATE tokens SET fish = fish + 1 WHERE cid = %i;" % int(cid)) + self.commit() + + def storeToken(self, cid, ato): + self.executeQuery('INSERT INTO tokens(cid, ato) VALUES(%i, "%s");' % (int(cid), ato)) + self.commit() + def dbHelper(): if setuptools.dbtype() == SQLITE: return dbObject(dbtype=SQLITE, path=setuptools.dbpath()) diff --git a/setup.py b/setup.py index 6355dcc..f1293a9 100755 --- a/setup.py +++ b/setup.py @@ -64,7 +64,7 @@ if not db.isInitialized(): db.executeQuery("CREATE TABLE names(`id` TEXT NOT NULL, `name` TEXT NOT NULL, `since` INTEGER NOT NULL, `until` INTEGER, PRIMARY KEY(id, until));") db.executeQuery("CREATE TABLE retweets(id INT PRIMARY KEY, author VARCHAR(30), created_at VARCHAR(30), text TEXT);") db.executeQuery("CREATE TABLE lyrics(id INTEGER PRIMARY KEY AUTOINCREMENT, text VARCHAR(140) NOT NULL, ref INT NOT NULL default '0', tweet_id INT, active BOOLEAN default '0');") - + db.executeQuery("CREATE TABLE tokens(`cid` INT PRIMARY KEY, `ato` TEXT, `ase` TEXT, `tweet` BOOLEAN DEFAULT 1, `fish` INT DEFAULT 0);") db.commit() db.closeConnection() @@ -75,8 +75,8 @@ print('''We are going to need a consumer key and consumer secret for accessing T If you don't have this yet, go to apps.twitter.com and create a new application. This application will need read/write access as well as access to direct messages.''') -cke = input("Consumer key: ") -cse = input("Consumer secret: ") +cke = input("Your Twitter application's consumer key: ") +cse = input("Your Twitter application's consumer secret: ") config.set("Twitter", "cke", cke) config.set("Twitter", "cse", cse) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 3199aeb..a290c80 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -8,7 +8,6 @@ class SetupException(Exception): def __str__(self): return "Seems like config.cfg has not been created yet or contains serious errors. Run setup.py to create it." - def getSetting(section, setting, path = "config.cfg"): config = configparser.RawConfigParser() config.read(path) @@ -89,6 +88,25 @@ def ase(section = TWITTER): except: raise SetupException() +def botname(): + try: + return getSetting("Telegram", "botname") + except: + return SetupException() + +def token(): + try: + return getSetting("Telegram", "token") + except: + return SetupException() + +def url(): + try: + return getSetting("Bot", "url") + except: + return SetupExecption() + + def dbCheck(db, create = False): if (not create and dbInitialized(db)) or (create and not dbInitialized(db)): return True diff --git a/strings.py b/strings.py new file mode 100644 index 0000000..3a67aff --- /dev/null +++ b/strings.py @@ -0,0 +1,57 @@ +auth = '''To get authenticated with Twitter, please visit this URL and sign in: + +* %s + +You will receive a six-digit PIN. Please send it to me like this: + +* /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. + +Please unauthenticate using /unauth first if you are sure you want to re-authenticate.''' + + +noauth = '''You are not authenticated. Please use /auth to sign in with Twitter.''' + + +start = '''Hey there! + +I'm @%s, everybody's favorite Twitter bot on Telegram! + +For me to help you, you will first have to authenticate with Twitter: + +* /auth + +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: + +* /toggletweet - Turn automatic tweeting of messages on/off. Useful in groups. +* /tweet TEXT - Explicitly tweet TEXT even if automatic tweeting is off (/toggletweet). +* /unauth - Revoke your authenticaton and stop using %s. + +Have fun!''' + + +toggleTweet = '''Automatic tweeting is now %s.''' + + +unauth = '''You're leaving already? :( + +I hope you had a good time with me. If there is anything you would like to tell me or my developers, please drop us a note at: + +* %s + +Your data has been deleted. Of course, you can always just re-authenticate using /auth. + +It was great having you here. So long, and thanks for all the /fish!''' + + +verify = '''Thanks for authenticating. You can now use all of my features!''' + +verifyfail = '''Oops, something went wrong during the authentication. Please try again: + +* /auth''' + +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.''' diff --git a/twitools/__init__.py b/twitools/__init__.py index 64d65f8..b765c30 100644 --- a/twitools/__init__.py +++ b/twitools/__init__.py @@ -1,4 +1,4 @@ -import tweepy, setuptools +import dbtools, tweepy, setuptools class twObject: @@ -69,3 +69,7 @@ def twoHelper(section = setuptools.TWITTER): def tweet(text, ref = 0, section = setuptools.TWITTER): return twoHelper(section).tweet(text, ref) + +def twoBotHelper(cid): + db = dbtools.dbHelper() + return twObject(ato = db.ato(cid), ase = db.ase(cid))