From 450d1bb49eb03b8d0c329e1b7fe8dc49cbf70039 Mon Sep 17 00:00:00 2001 From: NNTin Date: Sun, 10 Mar 2019 20:11:31 +0100 Subject: [PATCH] WIP towards yml configuration --- .gitignore | 3 +- bot/config.yml | 83 +++++++++++++++++++++++++++++++++++++++++++++++ bot/yml_parser.py | 66 +++++++++++++++++++++++++++++++++++++ requirements.txt | 2 ++ 4 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 bot/config.yml create mode 100644 bot/yml_parser.py diff --git a/.gitignore b/.gitignore index 8cf19681..e2cbc9b3 100644 --- a/.gitignore +++ b/.gitignore @@ -145,4 +145,5 @@ $RECYCLE.BIN/ *.xml .idea/discord-twitter-bot.iml experiment/ -bot/config.json \ No newline at end of file +bot/config.json +bot/.env \ No newline at end of file diff --git a/bot/config.yml b/bot/config.yml new file mode 100644 index 00000000..dcb8d29e --- /dev/null +++ b/bot/config.yml @@ -0,0 +1,83 @@ +###################################### +#### manual configuration example: ### +#Twitter: +# access_token: XXX-XXX +# access_token_secret: XXX +# consumer_key: XXX +# consumer_secret: XXX +# +#Discord: +# - IncludeReplyToUser: false +# IncludeRetweet: false +# IncludeUserReply: true +# custom_message: 'A new tweet!' +# keyword_sets: +# - - 'League' # tweet will be posted if tweet contains all 3 words (League of Legends), no particular order. +# - 'of' +# - 'Legends' +# - - 'Dota 2' # tweet will be posted if tweet contains 'Dota 2' in this specific order +# - - 'MOBA' # tweet will be posted if it contains 'MOBA' +# twitter_ids: +# - '123' # define as many as you want with a dash (-) at the beginning +# - '456' +# twitter_handles: +# - 'discordapp' +# twitter_lists: +# - 'https://twitter.com/rokxx/lists/dota-2' +# webhook_urls: +# - 'https://discordapp.com/api/webhooks/123456/XXXX-XXXX' +###################################### + +# configuration through environment variables +Twitter: + access_token: ${ACCESS_TOKEN} + access_token_secret: ${ACCESS_TOKEN_SECRET} + consumer_key: ${CONSUMER_KEY} + consumer_secret: ${CONSUMER_SECRET} + +Discord: + - IncludeReplyToUser: ${INCLUDE_REPLY_TO_USER} + IncludeRetweet: ${INCLUDE_RETWEET} + IncludeUserReply: ${INCLUDE_USER_REPLY} + custom_message: ${CUSTOM_MESSAGE} + keyword_sets: ${KEYWORDS} + twitter_ids: ${TWITTER_ID} + twitter_handles: ${TWITTER_HANDLE} + twitter_lists: ${TWITTER_LIST} + webhook_urls: ${WEBHOOK_URL} + - IncludeReplyToUser: ${INCLUDE_REPLY_TO_USER_2} + IncludeRetweet: ${INCLUDE_RETWEET_2} + IncludeUserReply: ${INCLUDE_USER_REPLY_2} + custom_message: ${CUSTOM_MESSAGE_2} + keyword_sets: ${KEYWORDS_2} + twitter_ids: ${TWITTER_ID_2} + twitter_handles: ${TWITTER_HANDLE_2} + twitter_lists: ${TWITTER_LIST_2} + webhook_urls: ${WEBHOOK_URL_2} + - IncludeReplyToUser: ${INCLUDE_REPLY_TO_USER_3} + IncludeRetweet: ${INCLUDE_RETWEET_3} + IncludeUserReply: ${INCLUDE_USER_REPLY_3} + custom_message: ${CUSTOM_MESSAGE_3} + keyword_sets: ${KEYWORDS_3} + twitter_ids: ${TWITTER_ID_3} + twitter_handles: ${TWITTER_HANDLE_3} + twitter_lists: ${TWITTER_LIST_3} + webhook_urls: ${WEBHOOK_URL_3} + - IncludeReplyToUser: ${INCLUDE_REPLY_TO_USER_4} + IncludeRetweet: ${INCLUDE_RETWEET_4} + IncludeUserReply: ${INCLUDE_USER_REPLY_4} + custom_message: ${CUSTOM_MESSAGE_4} + keyword_sets: ${KEYWORDS_4} + twitter_ids: ${TWITTER_ID_4} + twitter_handles: ${TWITTER_HANDLE_4} + twitter_lists: ${TWITTER_LIST_4} + webhook_urls: ${WEBHOOK_URL_4} + - IncludeReplyToUser: ${INCLUDE_REPLY_TO_USER_5} + IncludeRetweet: ${INCLUDE_RETWEET_5} + IncludeUserReply: ${INCLUDE_USER_REPLY_5} + custom_message: ${CUSTOM_MESSAGE_5} + keyword_sets: ${KEYWORDS_5} + twitter_ids: ${TWITTER_ID_5} + twitter_handles: ${TWITTER_HANDLE_5} + twitter_lists: ${TWITTER_LIST_5} + webhook_urls: ${WEBHOOK_URL_5} \ No newline at end of file diff --git a/bot/yml_parser.py b/bot/yml_parser.py new file mode 100644 index 00000000..c8898751 --- /dev/null +++ b/bot/yml_parser.py @@ -0,0 +1,66 @@ +from tweepy import OAuthHandler +from dotenv import load_dotenv +import yaml +import os +import re + + +class CustomFormatter(): + FALSE_STRINGS = ["false", "False", "f", "F", "0", "", "n", "N", "no", "No", "NO", "FALSE"] + + def to_bool(self, value: str) -> bool: + return False if value in self.FALSE_STRINGS else True + + def format(self, value): + return self.convert_field(*value.split("!")) + + def convert_field(self, value, conversion: str): + if isinstance(value, str): + if conversion == 's': # string is alrdy a string + return value + elif conversion == 'b': # turn string into a bool() + return self.to_bool(value) + elif conversion == 'l': # turn string into a list() + return value.split(',') + elif conversion == 'll': # turn string into a double list() + return [v.split('+') for v in value.split(',')] + else: + raise ValueError("Unknown conversion specifier {0!s}".format(conversion)) + else: + return None + + +CONFIG_YAML = os.path.abspath(os.path.join(os.path.dirname(__file__), "config.yml")) +DOTENV_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".env")) + +load_dotenv(dotenv_path=DOTENV_PATH) +config = dict() + +path_matcher = re.compile(r'\$\{([^}^{]+)\}') + + +def path_constructor(loader, node): + value = node.value + match = path_matcher.match(value) + env_var = match.group()[2:-1] + return os.environ.get(env_var, None) # + value[match.end():] + +yaml.add_implicit_resolver('!path', path_matcher) +yaml.add_constructor('!path', path_constructor) + +with open(CONFIG_YAML, 'r') as stream: + try: + config = yaml.load(stream) + except yaml.YAMLError as exc: + print(exc) + + +config['Discord'] = [{k: v for k, v in instance.items() if v is not None} for instance in config['Discord']] + + +auth = OAuthHandler(config["Twitter"]["consumer_key"], config["Twitter"]["consumer_secret"]) +auth.set_access_token(config["Twitter"]["access_token"], config["Twitter"]["access_token_secret"]) + +if __name__ == '__main__': + print(config) + print(os.environ.get("CONSUMER_KEY", None)) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 977eeb1c..0af56f96 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,8 @@ idna-ssl==1.1.0 multidict==4.5.2 oauthlib==3.0.1 PySocks==1.6.8 +python-dotenv==0.10.1 +PyYAML==3.13 requests==2.21.0 requests-oauthlib==1.2.0 six==1.12.0