Skip to content

Commit 90c0b42

Browse files
committed
migrated to new yaml config
1 parent 567c20a commit 90c0b42

13 files changed

+185
-27
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ target/
5959

6060
.idea/
6161
config.json
62+
config.yaml
6263
ignorelist
6364
log
6465

Pipfile

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ requests = ">=2.7.0"
99
requests-oauthlib = ">=0.5.0"
1010
python-dateutil = ">=2.4.2"
1111
"pushbullet.py" = ">=0.8.1"
12+
confuse = "*"
1213

1314
[dev-packages]
1415
requests-mock = "*"

Pipfile.lock

+27-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

requirements.txt

+2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
certifi==2018.1.18
22
chardet==3.0.4
3+
confuse==0.4.0
34
idna==2.6
45
oauthlib==2.0.7
56
pushbullet.py==0.11.0
67
python-dateutil==2.7.2
78
python-magic==0.4.15
9+
pyyaml==3.12
810
requests-oauthlib==0.8.0
911
requests==2.18.4
1012
six==1.11.0

tests/test_config.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
logging.disable(logging.ERROR)
99

1010

11-
class TestConfig(unittest.TestCase):
11+
class TestConfigJson(unittest.TestCase):
1212
config_data = '''{"search_queries":["test"],"follow_keywords":["test"],"fav_keywords":["test"],"consumer_key":"test","consumer_secret":"test","access_token_key":"test","access_token_secret":"test","retweet_interval":1,"retweet_random_margin":1,"scan_interval":1,"clear_queue_interval":1,"max_queue":1,"rate_limit_update_interval":1,"min_ratelimit_percent":1, "min_quote_similarity":0.80, "max_quote_depth": 5,"blocked_users_update_interval":1,"max_follows":1, "check_mentions_interval":1, "pushbullet_token": "test"}'''
1313

1414
def test_load(self):
@@ -41,3 +41,11 @@ def test_save_tokens(self):
4141
handle = m()
4242
handle.write.assert_any_call('"new_token"')
4343
handle.write.assert_any_call('"new_secret"')
44+
45+
46+
class TestConfig(unittest.TestCase):
47+
48+
def test_simple_config(self):
49+
Config()
50+
51+

yatcobot/actions.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class Follow(ActionABC):
2525

2626
def process(self, post):
2727
text = post['full_text']
28-
keywords = sum((create_keyword_mutations(x) for x in Config.follow_keywords), [])
28+
keywords = sum((create_keyword_mutations(x) for x in Config.get_config().actions.follow.keywords), [])
2929
if any(x in text.lower() for x in keywords):
3030
self.remove_oldest_follow()
3131
self.client.follow(post['user']['screen_name'])
@@ -38,7 +38,7 @@ def remove_oldest_follow(self):
3838

3939
follows = self.client.get_friends_ids()
4040

41-
if len(follows) > Config.max_follows:
41+
if len(follows) > Config.get_config().actions.follow.max_following:
4242
r = self.client.unfollow(follows[-1])
4343
logger.info('Unfollowed: {0}'.format(r['screen_name']))
4444

@@ -50,7 +50,7 @@ class Favorite(ActionABC):
5050

5151
def process(self, post):
5252
text = post['full_text']
53-
keywords = sum((create_keyword_mutations(x) for x in Config.fav_keywords), [])
53+
keywords = sum((create_keyword_mutations(x) for x in Config.get_config().actions.favorite.keywords), [])
5454
if any(x in text.lower() for x in keywords):
5555
r = self.client.favorite(post['id'])
5656
logger.info("Favorite: {0}".format(post['id']))

yatcobot/bot.py

+24-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import difflib
22
import logging
3+
from collections import OrderedDict
34

45
from .actions import Favorite, Follow
56
from .client import TwitterClient, TwitterClientRetweetedException
@@ -19,9 +20,10 @@ def __init__(self, ignore_list_file):
1920

2021
self.ignore_list = IgnoreList(ignore_list_file)
2122
self.post_queue = PostQueue()
22-
self.client = TwitterClient(Config.consumer_key, Config.consumer_secret,
23-
Config.access_token_key,
24-
Config.access_token_secret)
23+
self.client = TwitterClient(Config.get_config()['consumer_key'],
24+
Config.get_config()['consumer_secret'],
25+
Config.get_config()['access_token_key'],
26+
Config.get_config()['access_token_secret'])
2527
self.scheduler = PeriodicScheduler()
2628
self.notification = NotificationService()
2729
self.actions = [Follow(self.client), Favorite(self.client)]
@@ -60,7 +62,7 @@ def enter_contest(self):
6062
def clear_queue(self):
6163
"""Removes the extraneous posts from the post_queue"""
6264

63-
to_delete = len(self.post_queue) - Config.max_queue
65+
to_delete = len(self.post_queue) - Config.get_config().search.max_queue
6466

6567
if to_delete > 0:
6668
for i in range(to_delete):
@@ -82,9 +84,16 @@ def scan_new_contests(self):
8284

8385
logger.info("=== SCANNING FOR NEW CONTESTS ===")
8486

85-
for search_query in Config.search_queries:
87+
for search_query in Config.get_config().search.queries:
88+
89+
if isinstance(search_query, str):
90+
results = self.client.search_tweets(search_query, 50)
91+
elif isinstance(search_query, OrderedDict):
92+
search_query, settings = search_query.popitem()
93+
results = self.client.search_tweets(search_query, 50, language=settings['lang'])
94+
else:
95+
raise ValueError("Uknown type of query {}".format(str(search_query)))
8696

87-
results = self.client.search_tweets(search_query, 50, language=Config.search_language)
8897
logger.info("Got {} new results for: {}".format(len(results), search_query))
8998

9099
for post in results:
@@ -124,12 +133,13 @@ def check_new_mentions(self):
124133
def run(self):
125134
"""Run the bot as a daemon. This is blocking command"""
126135

127-
self.scheduler.enter(Config.clear_queue_interval, 1, self.clear_queue)
128-
self.scheduler.enter(Config.rate_limit_update_interval, 2, self.client.update_ratelimits)
129-
self.scheduler.enter(Config.blocked_users_update_interval, 3, self.update_blocked_users)
130-
self.scheduler.enter(Config.check_mentions_interval, 4, self.check_new_mentions)
131-
self.scheduler.enter(Config.scan_interval, 5, self.scan_new_contests)
132-
self.scheduler.enter_random(Config.retweet_interval, Config.retweet_random_margin, 6, self.enter_contest)
136+
self.scheduler.enter(Config.get_config().scheduler.clear_queue_interval, 1, self.clear_queue)
137+
self.scheduler.enter(Config.get_config().scheduler.rate_limit_update_interval, 2, self.client.update_ratelimits)
138+
self.scheduler.enter(Config.get_config().scheduler.blocked_users_update_interval, 3, self.update_blocked_users)
139+
self.scheduler.enter(Config.get_config().scheduler.check_mentions_interval, 4, self.check_new_mentions)
140+
self.scheduler.enter(Config.get_config().scheduler.search_interval, 5, self.scan_new_contests)
141+
self.scheduler.enter_random(Config.get_config().scheduler.retweet_interval,
142+
Config.get_config().scheduler.retweet_random_margin, 6, self.enter_contest)
133143

134144
# Init the program
135145
self.scheduler.run()
@@ -153,15 +163,15 @@ def _get_quoted_tweet(self, post):
153163
:param post: The post to check if its a quote
154164
:return: If it isnt a quote the argument, otherwise the original tweet
155165
"""
156-
for i in range(Config.max_quote_depth):
166+
for i in range(Config.get_config().search.max_quote_depth):
157167
# If it hasnt quote return the post
158168
if 'quoted_status' not in post:
159169
return post
160170

161171
quote = post['quoted_status']
162172
diff = difflib.SequenceMatcher(None, post['full_text'], quote['full_text']).ratio()
163173
# If the texts are similar continue
164-
if diff >= Config.min_quote_similarity:
174+
if diff >= Config.get_config().search.min_quote_similarity:
165175
logger.debug('{} is a quote, following to next post. Depth from original post {}'.format(post['id'], i))
166176
quote = self.client.get_tweet(quote['id'])
167177
# If its a quote of quote, get next quote and continue

yatcobot/cli.py

-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,5 @@ def main():
4040
create_logger(logging.INFO, args.logfile)
4141

4242
Config.load(args.config)
43-
4443
bot = Yatcobot(args.ignore_list)
4544
bot.run()

yatcobot/client.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def check_limit(self, endpoint):
6262
endpoint_limits = self[self._get_internal_endpoint_name(endpoint)]
6363

6464
# if over threshold sleep untill reset
65-
if endpoint_limits['percent'] < Config.min_ratelimit_percent:
65+
if endpoint_limits['percent'] < Config.get_config().min_ratelimit_percent:
6666

6767
reset_time = endpoint_limits['reset']
6868
now = int(datetime.datetime.now().strftime('%s'))

yatcobot/config.py

+72-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,78 @@
11
import json
2+
import confuse
23

34

4-
class Config:
5+
class Config(confuse.AttrDict):
6+
7+
template = {
8+
'consumer_key': confuse.String(),
9+
'consumer_secret': confuse.String(),
10+
'access_token_key': confuse.String(),
11+
'access_token_secret': confuse.String(),
12+
'min_ratelimit_percent': confuse.Integer(),
13+
14+
'search': {
15+
'queries': confuse.TypeTemplate(list),
16+
'priority_keywords': confuse.StrSeq(),
17+
'max_queue': confuse.Integer(),
18+
'max_quote_depth': confuse.Integer(),
19+
'min_quote_similarity': confuse.Number(),
20+
},
21+
22+
'actions': {
23+
'follow': {
24+
'enabled': confuse.TypeTemplate(bool),
25+
'keywords': confuse.StrSeq(),
26+
'max_following': confuse.Integer(),
27+
},
28+
'favorite': {
29+
'enabled': confuse.TypeTemplate(bool),
30+
'keywords': confuse.StrSeq()
31+
},
32+
},
33+
34+
'scheduler': {
35+
'search_interval': confuse.Integer(),
36+
'retweet_interval': confuse.Integer(),
37+
'retweet_random_margin': confuse.Integer(),
38+
'blocked_users_update_interval': confuse.Integer(),
39+
'clear_queue_interval': confuse.Integer(),
40+
'rate_limit_update_interval': confuse.Integer(),
41+
'check_mentions_interval': confuse.Integer(),
42+
},
43+
44+
'notifiers': {
45+
'pushbullet': {
46+
'enabled': confuse.TypeTemplate(bool),
47+
'token': confuse.String()
48+
}
49+
}
50+
}
51+
52+
_valid = None
53+
54+
@staticmethod
55+
def get_config():
56+
"""
57+
Gets the static config object
58+
:return:
59+
"""
60+
if Config._valid is None:
61+
raise ValueError("Configuration not loaded")
62+
return Config._valid
63+
64+
@staticmethod
65+
def load(filename):
66+
"""
67+
Loads a file and imports the settings
68+
:param filename: the file to import
69+
"""
70+
config = confuse.LazyConfig('Yatcobot', __name__)
71+
config.set_file(filename)
72+
Config._valid = config.get(Config.template)
73+
74+
75+
class ConfigJson:
576
"""Class that contains all config variables. It loads user values from a json file """
677

778
# Default values
@@ -42,8 +113,6 @@ def load(filename):
42113
if value == "":
43114
value = None
44115
setattr(Config, key, value)
45-
b = Config.search_language
46-
a = 5
47116

48117
@staticmethod
49118
def save_user_tokens(filename, token, secret):

yatcobot/config_default.yaml

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
min_ratelimit_percent: 10
2+
3+
consumer_key: null
4+
consumer_secret: null
5+
access_token_key: null
6+
access_token_secret: null
7+
8+
search:
9+
queries:
10+
- RT to win
11+
- Retweet and win
12+
- Διαγωνισμός:
13+
lang: el
14+
max_queue: 100
15+
max_quote_depth: 20
16+
min_quote_similarity: 0.5
17+
priority_keywords: ["ps4", "pc"]
18+
19+
actions:
20+
follow:
21+
enabled: true
22+
keywords: ["follow", "follower"]
23+
max_following: 1950
24+
favorite:
25+
enabled: true
26+
keywords: ["fav", "favorite"]
27+
28+
scheduler:
29+
search_interval: 5400
30+
retweet_interval: 600
31+
retweet_random_margin: 60
32+
blocked_users_update_interval: 300
33+
clear_queue_interval: 60
34+
rate_limit_update_interval: 60
35+
check_mentions_interval: 600
36+
37+
notifiers:
38+
pushbullet:
39+
enabled: false
40+
token: test
41+
42+

yatcobot/notifier.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,13 @@ def notify(self, title, message):
7272

7373
@staticmethod
7474
def is_enabled():
75-
if Config.pushbullet_token:
75+
if Config.get_config().notifiers.pushbullet.enabled:
7676
return True
7777
return False
7878

7979
@classmethod
8080
def from_config(cls):
81-
return cls(Config.pushbullet_token)
81+
return cls(Config.get_config().notifiers.pushbullet.enabled)
8282

8383
except ImportError:
8484
logger.warning("Could not import pushbullet.py. Pushbullet notification is disabled")

yatcobot/post_queue.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def get_keywords_score(self):
6161
score = 0
6262
text = post['full_text'].lower()
6363

64-
for keyword in Config.priority_keywords:
64+
for keyword in Config.get_config().search.priority_keywords:
6565
keyword = keyword.lower()
6666
score += text.count(keyword)
6767

0 commit comments

Comments
 (0)