thisisparker / cursewords

:pencil: Terminal-based crossword puzzle solving interface
GNU Affero General Public License v3.0
249 stars 30 forks source link

Twitch chat interactivity! #30

Open dansanderson opened 3 years ago

dansanderson commented 3 years ago

This PR introduces a major new feature set for Cursewords: Twitch chat interactivity! Cursewords can connect to a Twitch channel's chat room and react to messages posted by viewers. Features include:

This introduces a new configuration system for storing Twitch credentials and settings. It can use a configuration file in the TOML format, located in either the current working directory or in $HOME/.config/. Settings can be set or overridden on the command line as well. This system is flexible enough to use for future features.

See README.md for usage instructions.


Technical notes

This version uses twitchapps.com/tmi/ "OAuth tokens" for authentication. OIDC implicit code flow might be more user friendly (I think that's the one where you can open a browser and just sign in with the bot account?) but this works well enough if you're willing to copy-paste the token into config. I include instructions in the README.

The Twitch client runs in its own thread to avoid impeding the solving UI. It accesses Grid without explicit thread safety support, so I don't know if that'll be an issue. I considered a queue.Queue but so far this seems to work as is.

websockets uses asyncio coroutines. The coroutine event loop runs in the Twitch child thread. On exit, the main thread joins the Twitch thread so it can clean up. The Twitch thread also keeps track of who made correct guesses to print in an on-screen thank you message when quitting.

I could have used TwitchIO, an asyncio-based library with much of the chat bot functionality in twitch.py. I decided to try it with raw websockets because I thought I'd be able to figure out "whisper" support that TwitchIO v1 doesn't yet have. I concluded that whisper support requires "verified" bot status and decided to punt for now.

I wanted whisper support so that clue requests could be private and not spam the chat. With clues going to the public chat, I added a cooldown mechanism so a given user can only ask for a clue every so many seconds (configurable). I'm sure CursewordsLive attendees will be well behaved and this is probably overkill.

Outgoing messages use a delayed queue of one message per second to avoid tripping Twitch's rate limit for (unverified?) bots.

I left in some logging support that was useful for troubleshooting Twitch behaviors without cluttering the display. As submitted, it's disabled by default via config, and only logs IRC "NOTICE" messages. It could be used for other things.

I tried to keep intrusions on the main branch minimal: