| @@ -1 +1,2 @@ | |||||
| config.json | config.json | ||||
| docker-compose.yml | |||||
| @@ -0,0 +1,22 @@ | |||||
| FROM python:3.9-slim | |||||
| RUN useradd --create-home --shell /bin/bash bot | |||||
| WORKDIR /home/bot | |||||
| ENV PATH="/home/bot:${PATH}" | |||||
| # Prepare environment | |||||
| RUN python -m pip install --upgrade pip | |||||
| RUN pip install pipenv | |||||
| COPY Pipfile.lock . | |||||
| RUN pipenv sync && pipenv run pip freeze > requirements.txt | |||||
| # Add files | |||||
| RUN pip install -r requirements.txt && mkdir config | |||||
| COPY _twitchbot/ _twitchbot/ | |||||
| COPY bot.py . | |||||
| USER bot | |||||
| CMD ["python", "bot.py", "--config=config/config.json"] | |||||
| @@ -0,0 +1,68 @@ | |||||
| # Twason - The KISS Twitch bot | |||||
| Twason is an opinionated Twitch chatbot created with the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle) in mind. | |||||
| It is based on the [IRC](https://en.wikipedia.org/wiki/Internet_Relay_Chat) protocol and is configurable in just one JSON file. | |||||
| ## What the hell is that name!? | |||||
| Twason is an umbrella name based on two words: _Twitch_ (the platform the bot is designed for) and _Jason_ (as the JSON file that you use to wonfigure it). | |||||
| ## What features does it provide? | |||||
| Currently, Twason has the following features: | |||||
| - **Commands:** automatically answer to messages that start with a given command | |||||
| - Customizable commands prefix (useful if you're using multiple bots) | |||||
| - Mention the user who invoked the command in the answer | |||||
| - Help command auto-generation | |||||
| - **Timer:** automatically send pre-defined messages | |||||
| - Only one timer to keep the bot from spamming in the chat | |||||
| - Configurable time and number of messages between each automatic message | |||||
| - Two strategies available: | |||||
| - _round-robin_: send the messages in the same order they have been set in the configuration file | |||||
| - _shuffle_: send the messages in a random order | |||||
| More features will be available in the future. | |||||
| ## How do I use it? | |||||
| Twason is currently in development and may contain bugs, but it is globally usable. Actually, I'm using it on [my Twitch channel](https://twitch.tv/jdeuchnord). | |||||
| The simplest (and safest) way to use it is to use the Docker image: `deuchnord/twason`. | |||||
| A Docker-Compose file is also available for facility. | |||||
| ### About the Twitch token | |||||
| To enable the bot to connect to Twitch chat, you will need to generate a token. Head to the [Twitch Chat OAuth Password Generator](https://twitchapps.com/tmi/) and follow the instructions to generate it. | |||||
| Then, you will need to give it to the bot through the `TWITCH_TOKEN` environment variable. | |||||
| ### The JSON configuration file | |||||
| To configure the bot, you will need to create a JSON file in `config/config.json` as defined in the `docker-compose.yml` file. | |||||
| You can find a minimal configuration in the `config.json.dist` file in this repository. | |||||
| Below is the complete configuration reference: | |||||
| ```json5 | |||||
| { | |||||
| "nickname": "yourbot", // the Twitch name of your bot | |||||
| "channel": "yourchannel", // the channel the bot must follow | |||||
| "command_prefix": "!", // the prefix the commands will have (defaults to '!') | |||||
| "help": true, // if true, a help command will be automatically generated (defaults to true) | |||||
| "commands": [ // a list of commands that your bot will recognize and respond to (empty by default) | |||||
| { | |||||
| "name": "ping", // the command name - spaces are not recommended here (even though they are technically accepted) | |||||
| "message": "Pong @{author} Kappa" // the message the bot must send when someone invokes this command ('{author}' will be replaced with the user who invoked the command) | |||||
| } | |||||
| ], | |||||
| "timer": { // the configuration of the automatically sent messages | |||||
| "between": { | |||||
| "time": 10, // the minimum time that must have passed between two messages (defaults to 10) | |||||
| "messages": 10 // the minimum number of messages that the chat members must have sent between two messages (defaults to 10) | |||||
| }, | |||||
| "strategy": "round-robin", // the strategy used to send the messages: "round-robin" or "shuffle" (defaults to "round-robin") | |||||
| "messages": [ // the list of messages to send (empty by default) | |||||
| "Hello World! HeyGuys", | |||||
| ] | |||||
| } | |||||
| } | |||||
| ``` | |||||
| @@ -33,12 +33,12 @@ class Timer: | |||||
| time_between: int = 10, | time_between: int = 10, | ||||
| msgs_between: int = 10, | msgs_between: int = 10, | ||||
| strategy: TimerStrategy = TimerStrategy.ROUND_ROBIN, | strategy: TimerStrategy = TimerStrategy.ROUND_ROBIN, | ||||
| messages: [str] = [] | |||||
| messages: [str] = None | |||||
| ): | ): | ||||
| self.time_between = time_between | self.time_between = time_between | ||||
| self.msgs_between = msgs_between | self.msgs_between = msgs_between | ||||
| self.strategy = strategy | self.strategy = strategy | ||||
| self.messages = messages | |||||
| self.messages = messages if messages else [] | |||||
| @classmethod | @classmethod | ||||
| def from_dict(cls, param: dict): | def from_dict(cls, param: dict): | ||||
| @@ -98,7 +98,7 @@ class Config: | |||||
| ) | ) | ||||
| def get_config(): | |||||
| with open('config.json', 'r') as config_file: | |||||
| def get_config(file_path: str): | |||||
| with open(file_path, 'r') as config_file: | |||||
| token = environ['TWITCH_TOKEN'] | token = environ['TWITCH_TOKEN'] | ||||
| return Config.from_dict(json.loads(config_file.read()), token) | |||||
| return Config.from_dict(json.loads(config_file.read()), token) | |||||
| @@ -4,11 +4,13 @@ from .config import get_config, TimerStrategy | |||||
| from random import shuffle | from random import shuffle | ||||
| from datetime import datetime, timedelta | from datetime import datetime, timedelta | ||||
| config = None | |||||
| @irc3.plugin | @irc3.plugin | ||||
| class TwitchBot: | class TwitchBot: | ||||
| def __init__(self, bot): | def __init__(self, bot): | ||||
| self.config = get_config() | |||||
| self.config = config | |||||
| self.messages_stack = [] | self.messages_stack = [] | ||||
| self.bot = bot | self.bot = bot | ||||
| self.log = self.bot.log | self.log = self.bot.log | ||||
| @@ -1,5 +1,6 @@ | |||||
| #!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
| import argparse | |||||
| import irc3 | import irc3 | ||||
| from _twitchbot.config import get_config | from _twitchbot.config import get_config | ||||
| @@ -11,12 +12,14 @@ TWITCH_IRC_PORT = 6697 | |||||
| def main() -> int: | def main() -> int: | ||||
| config = get_config() | |||||
| print(config.timer.messages) | |||||
| args = get_arguments() | |||||
| twitchbot.config = get_config(args.config) | |||||
| print(twitchbot.config.timer.messages) | |||||
| bot = irc3.IrcBot.from_config({ | bot = irc3.IrcBot.from_config({ | ||||
| 'nick': config.nickname, | |||||
| 'password': config.token, | |||||
| 'autojoins': [config.channel], | |||||
| 'nick': twitchbot.config.nickname, | |||||
| 'password': twitchbot.config.token, | |||||
| 'autojoins': [twitchbot.config.channel], | |||||
| 'host': TWITCH_IRC_SERVER, | 'host': TWITCH_IRC_SERVER, | ||||
| 'port': TWITCH_IRC_PORT, | 'port': TWITCH_IRC_PORT, | ||||
| 'ssl': True, | 'ssl': True, | ||||
| @@ -28,5 +31,12 @@ def main() -> int: | |||||
| return 0 | return 0 | ||||
| def get_arguments(): | |||||
| parser = argparse.ArgumentParser() | |||||
| parser.add_argument('--config', '-c', type=str, default='config.json') | |||||
| return parser.parse_args() | |||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||
| exit(main()) | exit(main()) | ||||
| @@ -0,0 +1,22 @@ | |||||
| { | |||||
| "nickname": "yourbot", | |||||
| "channel": "yourchannel", | |||||
| "command_prefix": "!", | |||||
| "help": true, | |||||
| "commands": [ | |||||
| { | |||||
| "name": "ping", | |||||
| "message": "Pong @{author} Kappa" | |||||
| } | |||||
| ], | |||||
| "timer": { | |||||
| "between": { | |||||
| "time": 10, | |||||
| "messages": 10 | |||||
| }, | |||||
| "strategy": "round-robin", | |||||
| "messages": [ | |||||
| "Hello World! HeyGuys", | |||||
| ] | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,11 @@ | |||||
| version: '3.8' | |||||
| services: | |||||
| bot: | |||||
| build: | |||||
| dockerfile: Dockerfile | |||||
| context: . | |||||
| env: | |||||
| TWITCH_TOKEN: 'SET_THIS_TOKEN' | |||||
| volumes: | |||||
| - './config:/home/bot/config' | |||||