Improved logging & single-file executable
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@ __pycache__
|
|||||||
|
|
||||||
build/
|
build/
|
||||||
dist/
|
dist/
|
||||||
|
releases/
|
||||||
|
|
||||||
*.html
|
*.html
|
||||||
*.out
|
*.out
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
## 2020/05/10
|
## 2020/05/10
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
1. Adding basic initial logging to a file
|
1. Adding basic initial logging
|
||||||
|
* Logs both to a file and to the command prompt
|
||||||
|
|
||||||
Fixes: N/A
|
Fixes: N/A
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
|
set dest=".\dist\"
|
||||||
|
|
||||||
REM The below is an alternative to using a custom hook for praw
|
REM The below is an alternative to using a custom hook for praw
|
||||||
REM FOR /F "tokens=* USEBACKQ" %%F IN (`pipenv --venv`) DO (
|
REM FOR /F "tokens=* USEBACKQ" %%F IN (`pipenv --venv`) DO (
|
||||||
REM SET pipenvdir=%%F
|
REM SET pipenvdir=%%F
|
||||||
@@ -12,3 +14,10 @@ pyinstaller ^
|
|||||||
--onefile ^
|
--onefile ^
|
||||||
--additional-hooks-dir .\pyinstaller-hooks\ ^
|
--additional-hooks-dir .\pyinstaller-hooks\ ^
|
||||||
PointsBot.py
|
PointsBot.py
|
||||||
|
|
||||||
|
copy ".\README.md" %dest%
|
||||||
|
copy ".\LICENSE.md" %dest%
|
||||||
|
copy ".\CHANGELOG.md" %dest%
|
||||||
|
|
||||||
|
mkdir .\releases\
|
||||||
|
powershell Compress-Archive -Force .\dist\* .\releases\PointsBot_Windows_x64.zip
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
import praw
|
import praw
|
||||||
import prawcore
|
import prawcore
|
||||||
@@ -29,7 +30,12 @@ def run():
|
|||||||
print_welcome_message()
|
print_welcome_message()
|
||||||
|
|
||||||
cfg = config.load()
|
cfg = config.load()
|
||||||
logging.basicConfig(filename=cfg.log_path,
|
|
||||||
|
file_handler = logging.FileHandler(cfg.log_path, 'w', 'utf-8')
|
||||||
|
console_handler = logging.StreamHandler(sys.stderr)
|
||||||
|
console_handler.setLevel(logging.INFO)
|
||||||
|
# logging.basicConfig(filename=cfg.log_path,
|
||||||
|
logging.basicConfig(handlers=[file_handler, console_handler],
|
||||||
level=logging.DEBUG,
|
level=logging.DEBUG,
|
||||||
format='%(asctime)s %(levelname)s:%(module)s: %(message)s',
|
format='%(asctime)s %(levelname)s:%(module)s: %(message)s',
|
||||||
datefmt='%Y-%m-%d %H:%M:%S')
|
datefmt='%Y-%m-%d %H:%M:%S')
|
||||||
@@ -57,16 +63,20 @@ def run():
|
|||||||
else:
|
else:
|
||||||
logging.warning('Is NOT moderator for monitored subreddit')
|
logging.warning('Is NOT moderator for monitored subreddit')
|
||||||
|
|
||||||
monitor_comments(subreddit, db, levels)
|
monitor_comments(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 this one
|
||||||
except prawcore.exceptions.RequestException as e:
|
except prawcore.exceptions.RequestException as e:
|
||||||
log.error('Unable to connect; attempting again....')
|
logging.error('Unable to connect to Reddit')
|
||||||
|
logging.error('Error message: %s', e)
|
||||||
|
logging.error('Trying again')
|
||||||
except prawcore.exceptions.ServerError as e:
|
except prawcore.exceptions.ServerError as e:
|
||||||
log.error('Lost connection to Reddit; attempting to reconnect....')
|
logging.error('Lost connection to Reddit')
|
||||||
|
logging.error('Error message: %s', e)
|
||||||
|
logging.error('Attempting to reconnect')
|
||||||
|
|
||||||
|
|
||||||
def monitor_comments(subreddit, db, levels):
|
def monitor_comments(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 with each query
|
||||||
@@ -76,7 +86,8 @@ def monitor_comments(subreddit, db, levels):
|
|||||||
|
|
||||||
logging.info('Received comment')
|
logging.info('Received comment')
|
||||||
# TODO more debug info about comment, eg author
|
# TODO more debug info about comment, eg author
|
||||||
logging.debug('Comment text: "%s"', comm.body)
|
logging.debug('Comment author: "%s"', comm.author.name)
|
||||||
|
# logging.debug('Comment text: "%s"', comm.body)
|
||||||
|
|
||||||
if not marks_as_solved(comm):
|
if not marks_as_solved(comm):
|
||||||
logging.info('Comment does not mark issue as solved')
|
logging.info('Comment does not mark issue as solved')
|
||||||
@@ -103,17 +114,15 @@ def monitor_comments(subreddit, db, levels):
|
|||||||
level_info = level.user_level_info(points, levels)
|
level_info = level.user_level_info(points, levels)
|
||||||
|
|
||||||
# Reply to the comment marking the submission as solved
|
# Reply to the comment marking the submission as solved
|
||||||
reply_body = reply.make(
|
reply_body = reply.make(solver,
|
||||||
solver,
|
|
||||||
points,
|
points,
|
||||||
level_info,
|
level_info,
|
||||||
feedback_url=cfg.feedback_url,
|
feedback_url=cfg.feedback_url,
|
||||||
scoreboard_url=cfg.scoreboard_url
|
scoreboard_url=cfg.scoreboard_url)
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
comm.reply(reply_body)
|
comm.reply(reply_body)
|
||||||
logging.info('Replied to the comment')
|
logging.info('Replied to the comment')
|
||||||
logging.debug('Reply body: %s', reply_body)
|
# logging.debug('Reply body: %s', reply_body)
|
||||||
except praw.exceptions.APIException as e:
|
except praw.exceptions.APIException as e:
|
||||||
logging.error('Unable to reply to comment: %s', e)
|
logging.error('Unable to reply to comment: %s', e)
|
||||||
db.remove_point(solver)
|
db.remove_point(solver)
|
||||||
@@ -211,7 +220,7 @@ def log_solution_info(comm):
|
|||||||
logging.debug('Solution comment:')
|
logging.debug('Solution comment:')
|
||||||
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)
|
||||||
logging.debug('"Solved" comment:')
|
logging.debug('Comment marking solution as solved:')
|
||||||
logging.debug('Author: %s', comm.author.name)
|
logging.debug('Author: %s', comm.author.name)
|
||||||
logging.debug('Body: %s', comm.body)
|
logging.debug('Body: %s', comm.body)
|
||||||
|
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ class Config:
|
|||||||
def interactive_config(dest):
|
def interactive_config(dest):
|
||||||
configvals = {
|
configvals = {
|
||||||
'core': {},
|
'core': {},
|
||||||
|
'links': {},
|
||||||
'filepaths': {},
|
'filepaths': {},
|
||||||
'credentials': {},
|
'credentials': {},
|
||||||
'levels': [],
|
'levels': [],
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ EXCESS_POINTS = 100 # TODO move this to level and/or config?
|
|||||||
EXCESS_SYMBOL = '\u2605' # A star character
|
EXCESS_SYMBOL = '\u2605' # A star character
|
||||||
EXCESS_SYMBOL_TITLE = 'a star' # Used in comment body
|
EXCESS_SYMBOL_TITLE = 'a star' # Used in comment body
|
||||||
|
|
||||||
|
###
|
||||||
|
# TODO make this a ReplyFactory? pass in feedback & scoreboard URLs to the
|
||||||
|
# ReplyFactory constructor, then pass in redditor, points, level_info each time
|
||||||
|
# making a comment, ie probably make `make` or `build` a method of the factory
|
||||||
|
###
|
||||||
|
|
||||||
### Main Functions ###
|
### Main Functions ###
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user