Skip to content

Commit fcfe69e

Browse files
author
Soheil
committed
Implement stand alone script, add sherrgoo
1 parent ad2baf4 commit fcfe69e

34 files changed

+330
-240
lines changed

Procfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
worker: python app.py
1+
worker: python app.py

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ You can take a look at current scripts in `scripts` folder. Do not forget to add
3636
implementing.
3737

3838
## Testing
39-
Before submitting a pull request, test your feature. You can do it by running `test.py` and simulating the timeline.
39+
Before submitting a pull request, test your feature. You can do it by running `app.py` and simulating the timeline.
4040

4141
## Requirements
4242
You need `Python 3.4` or later and `twython 3.2` to run the code.

app.py

+14-63
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,22 @@
1-
#!/usr/bin/python
2-
# -*- coding: utf-8 -*-
1+
import threading
2+
from core.utils.logging import debug
33

4-
import time
4+
import standalone_runner
5+
import timeline_related_runner
56

6-
from twython import *
7-
from twython.exceptions import TwythonError, TwythonRateLimitError
87

9-
from core.timeline_scripts_manager import TimelineScriptsManager
10-
from core.utils.logging import log
11-
from core import settings
8+
class TimelineRelated(threading.Thread):
9+
def run(self):
10+
timeline_related_runner.run()
1211

1312

14-
class MyStreamer(TwythonStreamer):
15-
def __init__(self, *args, **kwargs):
16-
super(MyStreamer, self).__init__(
17-
settings.CONSUMER_KEY,
18-
settings.CONSUMER_SECRET,
19-
settings.OAUTH_TOKEN,
20-
settings.OAUTH_TOKEN_SECRET,
21-
*args, **kwargs
22-
)
23-
self.twitter = Twython(
24-
settings.CONSUMER_KEY,
25-
settings.CONSUMER_SECRET,
26-
settings.OAUTH_TOKEN,
27-
settings.OAUTH_TOKEN_SECRET
28-
)
29-
self.smart_handlers_manager = TimelineScriptsManager(self.twitter)
30-
31-
def is_a_tweet(self, data):
32-
'''
33-
This is a dirty way to do it, I know. But what else can I do?
34-
'''
35-
if 'text' in data and 'user' in data and 'id_str' in data and data['user'][
36-
'screen_name'] != settings.TWIZHOOSH_USERNAME:
37-
return True
38-
return False
39-
40-
def on_success(self, data):
41-
if self.is_a_tweet(data):
42-
log("Timeline update: %s [%s]" % (
43-
data['user']['screen_name'], data['id_str']))
44-
self.smart_handlers_manager.on_timeline_update(data)
45-
else:
46-
log("Got non status message: \n %s \n" % data)
47-
48-
def on_error(self, status_code, data):
49-
log(status_code)
50-
log(data)
51-
52-
def user(self, *args, **kwargs):
53-
while True:
54-
try:
55-
super(MyStreamer, self).user(self, *args, **kwargs)
56-
except TwythonRateLimitError as e:
57-
log("Rate limit error, retrying after {0} seconds".format(
58-
e.retry_after))
59-
time.sleep(e.retry_after)
60-
except TwythonError as e:
61-
log("Twython error {0}".format(e))
62-
63-
64-
def main():
65-
log("starting")
66-
stream = MyStreamer()
67-
stream.user(replies="all")
13+
class Standalone(threading.Thread):
14+
def run(self):
15+
standalone_runner.run()
6816

6917

7018
if __name__ == '__main__':
71-
main()
19+
t = TimelineRelated()
20+
s = Standalone()
21+
s.start()
22+
t.start()

core/on_demand_scripts_manager.py

+5-14
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,25 @@
11
#!/usr/bin/python
22
# -*- coding: utf-8 -*-
3-
import importlib
43
import re
54

6-
from core.scripts.timeline.base import base_handler
5+
from core.script_loader import load_scripts
6+
from core.scripts.timeline import base
77
from core.settings import INSTALLED_ON_DEMAND_SCRIPTS
88

99

10-
def load_command_parser_classes():
11-
scripts = []
12-
for script in INSTALLED_ON_DEMAND_SCRIPTS:
13-
(package_name, dot, class_name) = script.rpartition('.')
14-
module = importlib.import_module(package_name)
15-
scripts.append(getattr(module, class_name))
16-
return scripts
17-
18-
1910
# TODO: Every dispatcher should return a
2011

21-
class OnDemandScriptsManager(base_handler.BaseHandler):
12+
class OnDemandScriptsManager(base.BaseTimelineScript):
2213
is_mentioned_regex = r'.*(tw*izho*u*sh|[ت|ط][ی|ي][ظ|ز|ذ|ض][ه|ح]و*ش)\S* (?P<command>.*)'
2314

2415
def __init__(self, *args, **kwargs):
2516
super(OnDemandScriptsManager, self).__init__(*args, **kwargs)
2617

27-
script_classes = load_command_parser_classes()
18+
script_classes = load_scripts('scripts.on_demand', INSTALLED_ON_DEMAND_SCRIPTS)
2819
self.scripts = []
2920

3021
for script_class in script_classes:
31-
self.scripts.append(script_class(self.twitter, self.st_memory_manager))
22+
self.scripts.append(script_class())
3223

3324
def timeline_update(self, data):
3425
match = re.search(self.is_mentioned_regex, data['text'], re.IGNORECASE)

core/script_loader.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import importlib
2+
3+
4+
def underscore_to_camelcase(word):
5+
return ''.join(x.capitalize() for x in word.split('_'))
6+
7+
8+
def load_scripts(root_package, installed_scripts):
9+
"""
10+
Loads all script classes in: root_package
11+
:return: list of script classes
12+
"""
13+
scripts = []
14+
for script_name in installed_scripts:
15+
module_path = '{0}.{1}.{1}'.format(root_package, script_name)
16+
module = importlib.import_module(module_path)
17+
scripts.append(getattr(module, underscore_to_camelcase(script_name)))
18+
return scripts

core/scripts/on_demand/base.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from abc import abstractmethod
2+
3+
from core.scripts.timeline import base
4+
5+
6+
class BaseOnDemandScript(base.BaseTimelineScript):
7+
@abstractmethod
8+
def command_update(self, command, data):
9+
"""
10+
Called when OnDemandScriptsManager class receives a command
11+
"""
12+
pass
13+
14+
def timeline_update(self, data):
15+
super(BaseOnDemandScript, self).timeline_update(self, data)

core/scripts/on_demand/base/base_command_parser.py

-15
This file was deleted.

core/scripts/standalone/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__author__ = 'user'

core/scripts/standalone/base.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from abc import ABCMeta, abstractmethod
2+
3+
4+
class BaseStandaloneScript(metaclass=ABCMeta):
5+
@abstractmethod
6+
def on_called(self):
7+
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from abc import abstractmethod
2+
from random import randint
3+
from core import settings
4+
from core.scripts.standalone.base import BaseStandaloneScript
5+
from core.twitter_singleton import TwitterSingleton
6+
from core.utils.logging import debug
7+
8+
9+
class TweetPerDayBase(BaseStandaloneScript):
10+
expected_tweet_per_day = 10
11+
12+
def __init__(self):
13+
self.twitter = TwitterSingleton()
14+
15+
@abstractmethod
16+
def update(self):
17+
"""
18+
Called by on_called, approximately @expected_tweet_per_day times each day
19+
"""
20+
pass
21+
22+
def on_called(self):
23+
SECONDS_PER_DAY = 24 * 60 * 60
24+
num_of_calls = SECONDS_PER_DAY / settings.STAND_ALONE_REPEAT_TIME
25+
26+
if randint(0, num_of_calls) < self.expected_tweet_per_day:
27+
self.update()
28+
pass

core/scripts/timeline/base.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from abc import ABCMeta, abstractmethod
2+
3+
from core.st_memory_singleton import STMemory
4+
from core.twitter_singleton import TwitterSingleton
5+
6+
7+
class BaseTimelineScript(object, metaclass=ABCMeta):
8+
def __init__(self):
9+
self.twitter = TwitterSingleton()
10+
self.st_memory = STMemory()
11+
12+
@abstractmethod
13+
def timeline_update(self, data):
14+
"""
15+
Called when someone @Twizhoosh follows, tweets something
16+
"""
17+
pass

core/scripts/timeline/base/__init__.py

Whitespace-only changes.

core/scripts/timeline/base/base_handler.py

-41
This file was deleted.

core/scripts/timeline/smart_reply_by_keyword/smart_reply_by_keyword.py core/scripts/timeline/reply_by_keyword_base.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@
77

88
from twython import *
99

10-
from core.scripts.timeline.base.base_handler import BaseHandler
10+
from core.scripts.timeline.base import BaseTimelineScript
1111
from core.utils.logging import log
1212

1313

14-
class SmartReplyByKeyword(BaseHandler, metaclass=ABCMeta):
15-
'''
14+
class BaseReplyByKeywordScript(BaseTimelineScript, metaclass=ABCMeta):
15+
"""
1616
@replies contains a list of keywords and reply messages, If a tweet
1717
in timeline contains one of the keywords, @Twizhoosh replies with
1818
something random from its reply_messages
19-
'''
19+
"""
2020

2121
replies = []
2222

@@ -25,7 +25,7 @@ class SmartReplyByKeyword(BaseHandler, metaclass=ABCMeta):
2525

2626
@abstractmethod
2727
def timeline_update(self, data):
28-
marked = self.st_memory_manager.is_person_marked(self.__class__.__name__, data)
28+
marked = self.st_memory.is_person_marked(self.__class__.__name__, data)
2929

3030
if not self.answer_once or not marked:
3131
for i in range(len(self.replies)):
@@ -35,8 +35,8 @@ def timeline_update(self, data):
3535
log('matched')
3636
try:
3737
if self.answer_once:
38-
self.st_memory_manager.mark_person(self.__class__.__name__, data)
39-
self.reply_to(
38+
self.st_memory.mark_person(self.__class__.__name__, data)
39+
self.twitter.reply_to(
4040
data, random.choice(reply['reply_messages']))
4141
except TwythonError as e:
4242
print(e)

core/scripts/timeline/smart_reply_by_keyword/__init__.py

Whitespace-only changes.

core/settings.py

+12-12
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,23 @@
1010

1111
TWEET_LENGTH = 140
1212
TWEET_URL_LENGTH = 21
13-
14-
# TODO: Refactor the way modules are loaded. Since added some modules to
15-
# core, had to delete HANDLERS_PACKAGE
16-
1713
TWIZHOOSH_USERNAME = 'twizhoosh'
1814

15+
1916
# order is preserved
2017
INSTALLED_TIMELINE_SCRIPTS = [
21-
"core.on_demand_scripts_manager.OnDemandScriptsManager",
22-
23-
"scripts.timeline.spell_checker.spell_checker.SpellChecker",
24-
"scripts.timeline.goodnight.goodnight.GoodNight",
25-
"scripts.timeline.reply_by_learned_replies.reply_by_learned_replies.ReplyByLearnedReplies",
26-
# "scripts.timeline.say_hi.say_hi.SayHi"
18+
"spell_checker",
19+
"good_night",
20+
"reply_by_learned_replies",
2721
]
2822

2923
INSTALLED_ON_DEMAND_SCRIPTS = [
30-
"scripts.on_demand.learn_to_reply.learn_to_reply.LearnToReply",
31-
"scripts.on_demand.mustachify.mustachify.Mustachify",
24+
"learn_to_reply",
25+
"mustachify",
26+
]
27+
28+
INSTALLED_STANDALONE_SCRIPTS = [
29+
"call_sherrgoo",
3230
]
31+
# Stand-Alone repeat time (seconds)
32+
STAND_ALONE_REPEAT_TIME = 1

core/st_memory_manager.py core/st_memory_singleton.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
from core.utils.logging import log
22

33

4-
class STMemoryManager():
4+
class STMemory():
55
memory = {}
6+
_instance = None
7+
8+
def __new__(cls, *args, **kwargs):
9+
if not cls._instance:
10+
cls._instance = super(STMemory, cls).__new__(cls, *args, **kwargs)
11+
return cls._instance
612

713
def mark_person(self, key, tweet_data):
814
log('Mark {0} for {1}'.format(tweet_data['user']['screen_name'], key))

0 commit comments

Comments
 (0)