Package: twitterhal
TwitterHALA MegaHAL Twitter bot in Python.This project is in alpha, and should NOT be considered stable in any way.Live examples (in Swedish):@bibel3000,@trendhal3000PrerequisitesPython >= 3.6My fork of Chris Jones' Python MegaHALbear/python-twittercarpedm20/emojipython-LevenshteinOPTIONAL:detectlanguageOPTIONAL:redis-pyBut all those should be installed automatically bypiporsetup.py.Usepip install twitterhal[detectlanguage]to installdetectlanguage,pip install twitterhal[redis]to installredis-py.UsageCommand line$ twitterhal
usage: twitterhal [-s SETTINGS_MODULE] [-d] [-m] [-f] [-t]
                  [-r | --chat | --stats | --print-config | --post-random | --version]

optional arguments:
  -s SETTINGS_MODULE, --settings SETTINGS_MODULE
                        Python path to settings module. If omitted, we try
                        looking for it in the 'TWITTERHAL_SETTINGS_MODULE'
                        environment variable.
  -d, --debug           More verbose logging output
  -m, --include-mentions
                        Include all mentions in replies (rather than just the
                        handle we're replying to)
  -f, --force           Try and force stuff, even if TwitterHAL doesn't want
                        to
  -t, --test            Test mode; doesn't actually post anything
  -r, --run             Run the bot!
  --chat                Chat with the bot
  --stats               Display some stats
  --print-config        Print current parsed config
  --post-random         Post a new random tweet
  --version             Show program's version number and exittwitterhal --runwill post random tweets atrandom_post_times(see below), as well as answering all incoming mentions, all while trying its best not to exceed theTwitter API rate limits.As a libraryfromtwitterhalimportTwitterHALwithTwitterHAL(screen_name="twitterhal")ashal:formentioninhal.get_new_mentions():hal.generate_reply(mention)hal.generate_random()hal.post_from_queue()ConfigurationSettings are read from a Python module specified in theTWITTERHAL_SETTINGS_MODULEenvironment variable, or whatever module you supply to the command-line utility via the[-s | --settings]parameter.Some example settings:SCREEN_NAME="my_k3wl_twitter_user"RANDOM_POST_TIMES=[datetime.time(8),datetime.time(16),datetime.time(22)]INCLUDE_MENTIONS=TrueDETECTLANGUAGE_API_KEY=""DATABASE={"class":"path.to.DatabaseClass","options":{},"test_options":{},}BANNED_USERS=["my_other_twitterhal_bot"]RUNNER_SLEEP_SECONDS=5POST_STATUS_LIMIT=300POST_STATUS_LIMIT_RESET_FREQUENCY=3*60*60TWITTER_API={"consumer_key":"foo","consumer_secret":"bar","access_token_key":"boo","access_token_secret":"far","timeout":40,"tweet_mode":"extended",}MEGAHAL_API={"max_length":twitter.api.CHARACTER_LIMIT,"brainfile":"twitterhal-brain","order":megahal.DEFAULT_ORDER,"timeout":megahal.DEFAULT_TIMEOUT,"banwords":["MOST","COMMON","WORDS"],}BANNED_USERS: List of Twitter usernames (handles), without leading "@". We will never respond to, or mention, these users. Useful if you, for example, run two bots and don't want them to get stuck in an eternal loop responding to each other. (Perhaps, someday, I will figure out a clever way to detect such loops automatically.)DATABASE: A dict of info about the database backend. Must at least contain the keyclass, which must be the path of a class inheriting fromdatabase.BaseDatabase. Included aredatabase.ShelveDatabaseanddatabase.RedisDatabase. Theoptionskey contains kwargs to be sent to that database class'__init__()method. When TwitterHAL is run with the--testoption, the options will be extended with the contents of thetest_optionsdict.INCLUDE_MENTIONS: ifTrue, TwitterHAL will includeallmentions in its replies. That is, not only the @handle of the user who wrote to it, but also every user they mentioned in their tweet. Perhaps you should use this carefully. Anyway, the default isFalse.MEGAHALcontains keyword arguments formegahal.Megahal. Consultthat modulefor more info.MEGAHAL_API["banwords"]: you may want to set this if your bot will not be speaking English. Pro tip: search for a list of the ~300 most commonly used words in your language, and use those.POST_STATUS_LIMITandPOST_STATUS_LIMIT_RESET_FREQUENCY: For some reason, Twitter's API doesn't provide info about the current ratio limits for posting tweets (and retweets), so I had to implement that check myself to my best ability. The numbers are taken fromhere.RANDOM_POST_TIMES: TwitterHAL will post a randomly generated tweet on those points of (local) time every day. Default: 8:00, 16:00, and 22:00 (that is 8 AM, 4 PM and 10 PM, for those of you stuck in antiquity).RUNNER_SLEEP_SECONDS: The interval with whichruntime.runnerstarts itsloop tasks. See below.TWITTER_APIcontains keyword arguments fortwitter.Api. Read more about ithere.ExtendingPersistent storageYou may extend TwitterHAL's database by subclassingTwitterHALand addingdatabase.DatabaseItemdefinitions to itsinit_db()method. Maybe you want to feed the MegaHAL brain by regularily fetching top tweets for trending topics, and need to keep track of those? I know I do.By default, the database (which is a subtype ofdatabase.BaseDatabase) will contain:posted_tweets(models.TweetList): List of posted Tweetsmentions(models.TweetList): List of tweets that mention us, and whether they have been answeredLanguage detectionTweets are internally stored inmodels.TweetList, which contains the methodonly_in_language(). This will filter out all tweets that areprobablyin the chosen language, with the help of theLanguage Detection API. Justpip install detectlanguage, get yourself an API key and feed it todetectlanguage.configuration.api_key(or set it in your settings; see above), and you're all set.Twitter API callsIf you extend TwitterHAL with new methods that call the Twitter API, it's recommended you also check TwitterHAL'scan_do_request(url), whereurlis something like/statuses/mentions_timeline(consultthis pagefor full list), to see whether this call should be made at this time.RuntimeThe "daemon" (not really a daemon)twitterhal.runtime.runner, invoked bytwitterhal --run, does these things:Startsworkers, which will run continuously in separate threadsWith an interval ofsettings.RUNNER_SLEEP_SECONDSseconds (default: 5), runsloop tasks, each in a new threadOn exit, runspost loop tasksWorkersare registered byTwitterHAL.register_workers()throughrunner.register_worker(), and should be callables that loop until interrupted by a signal (seeGracefulKillersection below). If they accept the boolean keyword argumentrestart, they will be executed withrestart=Truein case they exited prematurely and had to be restarted by the runner.Loop tasks, unlike workers, should be finite in time. They are registered byTwitterHAL.register_loop_tasks()throughrunner.register_loop_task(), are run max once per loop, and can be any callable. Ifrunner.register_loop_task()is called with the integer argumentsleep(seconds),GracefulKiller.sleep()(see below) will be called at the end of every execution of this task, and the runner will be prohibited from starting new executions of the task until this one has finished. (If you don't want it to sleep at the end, but still want to block the task from being run multiple times concurrently, sendsleep=0.)Post loop tasksare registrered byTwitterHAL.register_post_loop_tasks()throughrunner.register_post_loop_task(), and can be any callable. They are called after the loop has been interrupted, and arenotrun in separate threads. Useful for various clean-up actions. By default, there are none.GracefulKillergracefulkiller.killeris an object that listens forSIGINTandSIGTERMsignals, whereupon itskill_nowattribute is set toTrue. It also has asleep()method, that mimicstime.sleep()but aborts max 1 second after one of the aforementioned signals has been caught.sleep()returnsTrueifSIGALRMwas caught sometime during the sleeping, which could be used for pinging. Feel free to use this in yourworkers,loop tasks, etc.Example:fromtwitterhal.gracefulkillerimportkillerdefhello_world_worker():whilenotkiller.kill_now:print("Hello, world!")ping=killer.sleep(10)ifping:print("Pong!")Q & AWhy doesn't TwitterHAL see all my mentions?Twitter has a setting called "quality filter", which is said to "filter lower-quality content from your notifications", and is turned on by default. You can go to your bot'snotifications settingsand uncheck the "Quality filter" checkbox (at least, that's how you did it 2020-04-24). This should solve it.