Cleanup & ideas

This commit is contained in:
Collin Rapp
2020-08-25 22:23:16 -07:00
parent df63269739
commit 2387e67004
13 changed files with 163 additions and 24 deletions

5
.gitignore vendored
View File

@@ -4,10 +4,7 @@
*.pyc *.pyc
__pycache__ __pycache__
build/
dist/
releases/
*.html *.html
*.out *.out
*.swp *.swp
/.vscode/

View File

@@ -4,6 +4,7 @@ url = "https://pypi.org/simple"
verify_ssl = true verify_ssl = true
[dev-packages] [dev-packages]
pylint = "*"
[packages] [packages]
toml = "*" toml = "*"

116
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "c5de2f495184b77e04ef9eeabbf802ca765c0a28bc36b8e6f84da0dcac053224" "sha256": "eb0083fecb64a3387eba2a1da2fb9fd3600b34c11989342104f415e9c2568676"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@@ -96,5 +96,117 @@
"version": "==0.57.0" "version": "==0.57.0"
} }
}, },
"develop": {} "develop": {
"astroid": {
"hashes": [
"sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703",
"sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386"
],
"version": "==2.4.2"
},
"colorama": {
"hashes": [
"sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff",
"sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"
],
"markers": "sys_platform == 'win32'",
"version": "==0.4.3"
},
"isort": {
"hashes": [
"sha256:60a1b97e33f61243d12647aaaa3e6cc6778f5eb9f42997650f1cc975b6008750",
"sha256:d488ba1c5a2db721669cc180180d5acf84ebdc5af7827f7aaeaa75f73cf0e2b8"
],
"version": "==5.4.2"
},
"lazy-object-proxy": {
"hashes": [
"sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d",
"sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449",
"sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08",
"sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a",
"sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50",
"sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd",
"sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239",
"sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb",
"sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea",
"sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e",
"sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156",
"sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142",
"sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442",
"sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62",
"sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db",
"sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531",
"sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383",
"sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a",
"sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357",
"sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4",
"sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"
],
"version": "==1.4.3"
},
"mccabe": {
"hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
],
"version": "==0.6.1"
},
"pylint": {
"hashes": [
"sha256:bb4a908c9dadbc3aac18860550e870f58e1a02c9f2c204fdf5693d73be061210",
"sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f"
],
"index": "pypi",
"version": "==2.6.0"
},
"six": {
"hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
"version": "==1.15.0"
},
"toml": {
"hashes": [
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
"sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
],
"index": "pypi",
"version": "==0.10.1"
},
"typed-ast": {
"hashes": [
"sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355",
"sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919",
"sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa",
"sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652",
"sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75",
"sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01",
"sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d",
"sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1",
"sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907",
"sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c",
"sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3",
"sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b",
"sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614",
"sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb",
"sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b",
"sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41",
"sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6",
"sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34",
"sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe",
"sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
"sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
],
"markers": "implementation_name == 'cpython' and python_version < '3.8'",
"version": "==1.4.1"
},
"wrapt": {
"hashes": [
"sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"
],
"version": "==1.12.1"
}
}
} }

View File

View File

@@ -2,6 +2,8 @@
## General ## General
* [X] Initial/Basic Logging
* Especially when unable to handle a comment
## File-Specific ## File-Specific
### bot.py ### bot.py

View File

@@ -20,3 +20,22 @@ To ensure that a point is awarded to the correct user:
could simple check that one of the comments in the comment tree belongs to could simple check that one of the comments in the comment tree belongs to
that user, and then award them the point. that user, and then award them the point.
- Honestly, this is probably overcomplicated and unnecessary, though. - Honestly, this is probably overcomplicated and unnecessary, though.
## Multiple behaviors
Implement the concept of actions, with a mapping between trigger keywords/patterns and actions. This will allow for multiple behaviors (eg mark as solved for one behavior, and just show points when summoned for another).
Maybe allow chains of actions. For example:
1. OP comments "!solved"
2. Bot receives comment
3. Action 1: Determine whether is "!solved" comment
4. Action 2: Determine solver
5. Action 3: Add points
6. Action 4: Reply
## Making the bot easier to configure
Allow bot user to just specify a list of strings to recognize to trigger the bot's response. Allow user to specify whether case-sensitive. Ways to do this, with varying performance:
* Just search each text for each keyword;
* Use `re.compile()` for each keyword before searching (*may* be faster?); or
* Build a binary search tree for all keywords for each action

View File

@@ -2,4 +2,3 @@
## End-to-end Testing Scenarios ## End-to-end Testing Scenarios

View File

@@ -6,10 +6,17 @@ File-specific lists are in loose descending order of priority.
n/a n/a
## Bugs
* [ ] Users can get multiple points for same solution
- Scenario:
- OP comments !solved
- OP deletes !solved comment
- OP comments !solved again
- Hopefully this will be easily solved once db migrations are working
## General ## General
* [ ] Logging
* Especially when unable to handle a comment
* [ ] Notifications * [ ] Notifications
* Let admins know if a comment can't be properly handled * Let admins know if a comment can't be properly handled
* Email preferable; could do Reddit message, too * Email preferable; could do Reddit message, too
@@ -17,6 +24,7 @@ n/a
* [ ] Testing * [ ] Testing
- Any PRAW model that inherits from `praw.PRAWBase` has a `parse` method - Any PRAW model that inherits from `praw.PRAWBase` has a `parse` method
that could perhaps be used to make fake objects for testing that could perhaps be used to make fake objects for testing
- Add regression tests for comments, etc.?
* [ ] GUI * [ ] GUI
- [ ] Create a GUI for configuring and running the bot, and performing other jobs - [ ] Create a GUI for configuring and running the bot, and performing other jobs
like adding or subtracting points for specific redditors like adding or subtracting points for specific redditors
@@ -52,6 +60,7 @@ n/a
### bot.py ### bot.py
* [ ] (Maybe) sanitize input text?
* [ ] Now that the date of each solution is being stored, can check for missed * [ ] Now that the date of each solution is being stored, can check for missed
submissions each time the bot is run by searching subreddit history until submissions each time the bot is run by searching subreddit history until
last solution found last solution found

View File

@@ -47,21 +47,26 @@ def run():
password=cfg.password, password=cfg.password,
user_agent=USER_AGENT) user_agent=USER_AGENT)
logging.info('Connected to Reddit as %s', reddit.user.me()) logging.info('Connected to Reddit as %s', reddit.user.me())
if not reddit.read_only: access_type = 'read-only' if reddit.read_only else 'write'
logging.info('Has write access to Reddit') logging.info(f'Has {access_type} access to Reddit')
else: # if not reddit.read_only:
logging.warning('Has read-only access to Reddit') # logging.info('Has write access to Reddit')
# else:
# logging.warning('Has read-only access to Reddit')
subreddit = reddit.subreddit(cfg.subreddit) subreddit = reddit.subreddit(cfg.subreddit)
logging.info('Watching subreddit %s', subreddit.title) logging.info('Watching subreddit %s', subreddit.title)
if subreddit.moderator(redditor=reddit.user.me()): is_mod = subreddit.moderator(redditor=reddit.user.me())
logging.info('Is moderator for monitored subreddit') logging.info(f'Is {"" if is_mod else "NOT "} moderator for subreddit')
else: # if subreddit.moderator(redditor=reddit.user.me()):
logging.warning('Is NOT moderator for monitored subreddit') # logging.info('Is moderator for monitored subreddit')
# else:
# logging.warning('Is NOT moderator for monitored subreddit')
monitor_comments(reddit, subreddit, db, levels, cfg) monitor_comments(reddit, subreddit, db, levels, cfg)
# Ignoring other potential exceptions for now, since we may not be able # Ignoring other potential exceptions for now, since we may not be able
# to recover from them as well as from this one # to recover from them as well as from these ones
except prawcore.exceptions.RequestException as e: except prawcore.exceptions.RequestException as e:
logging.error('Unable to connect to Reddit') logging.error('Unable to connect to Reddit')
logging.error('Error message: %s', e) logging.error('Error message: %s', e)
@@ -75,7 +80,7 @@ def run():
def monitor_comments(reddit, subreddit, db, levels, cfg): def monitor_comments(reddit, subreddit, db, levels, cfg):
"""Monitor new comments in the subreddit, looking for confirmed solutions.""" """Monitor new comments in the subreddit, looking for confirmed solutions."""
# Passing pause_after=0 will bypass the internal exponential delay, but have # Passing pause_after=0 will bypass the internal exponential delay, but have
# to check if any comments are returned with each query # to check if any comments are returned after each query
for comm in subreddit.stream.comments(skip_existing=True, pause_after=0): for comm in subreddit.stream.comments(skip_existing=True, pause_after=0):
if comm is None: if comm is None:
continue continue
@@ -288,4 +293,3 @@ def log_solution_info(comm):
logging.debug('Author: %s', comm.parent().author.name) logging.debug('Author: %s', comm.parent().author.name)
logging.debug('Body: %s', comm.parent().body) logging.debug('Body: %s', comm.parent().body)

View File

@@ -191,4 +191,3 @@ def interactive_config(dest):
toml.dump(configvals, f) toml.dump(configvals, f)
print('#' * 80 + f'\nConfig settings saved to {dest}\n' + '#' * 80) print('#' * 80 + f'\nConfig settings saved to {dest}\n' + '#' * 80)

View File

@@ -104,4 +104,3 @@ class Database:
return points return points

View File

@@ -40,4 +40,3 @@ def user_level_info(points, levels):
def is_max_level(level_info): def is_max_level(level_info):
return not level_info.next return not level_info.next

View File

@@ -153,4 +153,3 @@ def footer(feedback_url=None, scoreboard_url=None):
# '| [Feedback](https://forms.gle/m94aGjFQwGopqQ836) ' # '| [Feedback](https://forms.gle/m94aGjFQwGopqQ836) '
# '| [Source Code](https://github.com/cur33/PointsBot))') # '| [Source Code](https://github.com/cur33/PointsBot))')