Freeze the app for user-friendly distribution & usage
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,6 +4,9 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
__pycache__
|
__pycache__
|
||||||
|
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
|
||||||
*.html
|
*.html
|
||||||
*.out
|
*.out
|
||||||
*.swp
|
*.swp
|
||||||
|
|||||||
1
Pipfile
1
Pipfile
@@ -4,6 +4,7 @@ url = "https://pypi.org/simple"
|
|||||||
verify_ssl = true
|
verify_ssl = true
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
pyinstaller = "*"
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
toml = "*"
|
toml = "*"
|
||||||
|
|||||||
81
Pipfile.lock
generated
81
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "c5de2f495184b77e04ef9eeabbf802ca765c0a28bc36b8e6f84da0dcac053224"
|
"sha256": "d2ab36131af79e2a9efe43cbaf017a804bbf43d83863ec9ff3cba1987535ee30"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -18,10 +18,10 @@
|
|||||||
"default": {
|
"default": {
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
|
"sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304",
|
||||||
"sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
|
"sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"
|
||||||
],
|
],
|
||||||
"version": "==2019.11.28"
|
"version": "==2020.4.5.1"
|
||||||
},
|
},
|
||||||
"chardet": {
|
"chardet": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -32,32 +32,33 @@
|
|||||||
},
|
},
|
||||||
"idna": {
|
"idna": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
|
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
|
||||||
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
|
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
|
||||||
],
|
],
|
||||||
"version": "==2.8"
|
"version": "==2.9"
|
||||||
},
|
},
|
||||||
"praw": {
|
"praw": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:252246f8ea2ae6fba59bbf45de3fed568a80c086bca66747a2745dff5e11df4a",
|
"sha256:65129169d560800261908415ed955f3cbc63648549820b3ccce0a823ffa2fd78",
|
||||||
"sha256:544904cb821afff43c22e2dad4245658d41135d84b3a9463a5e29dd132da6efe"
|
"sha256:74e4b6c3f206342d05272ce1770ac7b9c48207c9a7ffea3d5251460b70f18188",
|
||||||
|
"sha256:dcdcf13b7f7ae2393afd914644bf16b254eaf5230c81adf2feafe1ec514307ca"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==6.5.1"
|
"version": "==7.0.0"
|
||||||
},
|
},
|
||||||
"prawcore": {
|
"prawcore": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:25dd14bf121bc0ad2ffc78e2322d9a01a516017105a5596cc21bb1e9a928b40c",
|
"sha256:a982a49bc911fe0e3a9751319091c380f79d5e1ba1ba19cb8dbbce21ad8b0ca7",
|
||||||
"sha256:ab5558efb438aa73fc66c4178bfc809194dea3ce2addf4dec873de7e2fd2824e"
|
"sha256:b907843ab969d759cbc03f1f749acea24d11859d6aed447b2fa1cd0eda9ecf34"
|
||||||
],
|
],
|
||||||
"version": "==1.0.1"
|
"version": "==1.3.0"
|
||||||
},
|
},
|
||||||
"requests": {
|
"requests": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
|
"sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
|
||||||
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
|
"sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
|
||||||
],
|
],
|
||||||
"version": "==2.22.0"
|
"version": "==2.23.0"
|
||||||
},
|
},
|
||||||
"six": {
|
"six": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -76,17 +77,17 @@
|
|||||||
},
|
},
|
||||||
"update-checker": {
|
"update-checker": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:59cfad7f9a0ee99f95f1dfc60f55bf184937bcab46a7270341c2c33695572453",
|
"sha256:1ff5dc7aab340b4f7710bd6c69d08ff5a5351617cd4ba0eb8886ddb285e2104f",
|
||||||
"sha256:70e39446fccf77b21192cf7a8214051fa93a636dc3b5c8b602b589d100a168b8"
|
"sha256:2def8db7f63bd45c7d19df5df570f3f3dfeb1a1f050869d7036529295db10e62"
|
||||||
],
|
],
|
||||||
"version": "==0.16"
|
"version": "==0.17"
|
||||||
},
|
},
|
||||||
"urllib3": {
|
"urllib3": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
|
"sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527",
|
||||||
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
|
"sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"
|
||||||
],
|
],
|
||||||
"version": "==1.25.8"
|
"version": "==1.25.9"
|
||||||
},
|
},
|
||||||
"websocket-client": {
|
"websocket-client": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -96,5 +97,39 @@
|
|||||||
"version": "==0.57.0"
|
"version": "==0.57.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"develop": {}
|
"develop": {
|
||||||
|
"altgraph": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1f05a47122542f97028caf78775a095fbe6a2699b5089de8477eb583167d69aa",
|
||||||
|
"sha256:c623e5f3408ca61d4016f23a681b9adb100802ca3e3da5e718915a9e4052cebe"
|
||||||
|
],
|
||||||
|
"version": "==0.17"
|
||||||
|
},
|
||||||
|
"future": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
|
||||||
|
],
|
||||||
|
"version": "==0.18.2"
|
||||||
|
},
|
||||||
|
"pefile": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:a5d6e8305c6b210849b47a6174ddf9c452b2888340b8177874b862ba6c207645"
|
||||||
|
],
|
||||||
|
"version": "==2019.4.18"
|
||||||
|
},
|
||||||
|
"pyinstaller": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3730fa80d088f8bb7084d32480eb87cbb4ddb64123363763cf8f2a1378c1c4b7"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==3.6"
|
||||||
|
},
|
||||||
|
"pywin32-ctypes": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942",
|
||||||
|
"sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"
|
||||||
|
],
|
||||||
|
"version": "==0.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
PointsBot.py
Normal file
3
PointsBot.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
"""Dummy file used for either runnning or freezing the bot."""
|
||||||
|
import pointsbot
|
||||||
|
pointsbot.run()
|
||||||
37
PointsBot.spec
Normal file
37
PointsBot.spec
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(['PointsBot.py'],
|
||||||
|
pathex=['C:\\Users\\Collin\\Documents\\git\\PointsBot'],
|
||||||
|
binaries=[],
|
||||||
|
datas=[],
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=['.\\pyinstaller-hooks\\'],
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data,
|
||||||
|
cipher=block_cipher)
|
||||||
|
exe = EXE(pyz,
|
||||||
|
a.scripts,
|
||||||
|
[],
|
||||||
|
exclude_binaries=True,
|
||||||
|
name='PointsBot',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
console=True )
|
||||||
|
coll = COLLECT(exe,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
name='PointsBot')
|
||||||
13
freeze.cmd
Normal file
13
freeze.cmd
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
REM FOR /F "tokens=* USEBACKQ" %%F IN (`pipenv --venv`) DO (
|
||||||
|
REM SET pipenvdir=%%F
|
||||||
|
REM )
|
||||||
|
|
||||||
|
REM --add-data "%pipenvdir%\Lib\site-packages\praw\praw.ini;site-packages\praw\praw.ini" ^
|
||||||
|
|
||||||
|
REM --noconfirm ^
|
||||||
|
pyinstaller ^
|
||||||
|
--onedir ^
|
||||||
|
--additional-hooks-dir .\pyinstaller-hooks\ ^
|
||||||
|
PointsBot.py
|
||||||
5
freeze.sh
Normal file
5
freeze.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
pyinstaller \
|
||||||
|
--noconfirm \
|
||||||
|
--onedir \
|
||||||
|
--additional-hooks-dir ./pyinstaller-hooks/ \
|
||||||
|
PointsBot.py
|
||||||
129
pointsbot/bot.py
129
pointsbot/bot.py
@@ -1,3 +1,6 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import praw
|
import praw
|
||||||
@@ -9,8 +12,13 @@ from . import config, database, level, reply
|
|||||||
|
|
||||||
USER_AGENT = 'PointsBot (by u/GlipGlorp7)'
|
USER_AGENT = 'PointsBot (by u/GlipGlorp7)'
|
||||||
|
|
||||||
|
# TODO put this in config
|
||||||
|
LOG_FILEPATH = os.path.abspath(os.path.join(os.path.expanduser('~'),
|
||||||
|
'.pointsbot',
|
||||||
|
'pointsbot.log.txt'))
|
||||||
|
|
||||||
# The pattern that determines whether a post is marked as solved
|
# The pattern that determines whether a post is marked as solved
|
||||||
# Could also just use re.IGNORECASE flag
|
# Could also use re.IGNORECASE flag instead
|
||||||
SOLVED_PAT = re.compile('![Ss]olved')
|
SOLVED_PAT = re.compile('![Ss]olved')
|
||||||
MOD_SOLVED_PAT = re.compile('/[Ss]olved')
|
MOD_SOLVED_PAT = re.compile('/[Ss]olved')
|
||||||
|
|
||||||
@@ -18,6 +26,13 @@ MOD_SOLVED_PAT = re.compile('/[Ss]olved')
|
|||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
|
logging.basicConfig(filename=LOG_FILEPATH,
|
||||||
|
level=logging.DEBUG,
|
||||||
|
format='%(asctime)s %(module)s:%(levelname)s: %(message)s',
|
||||||
|
datefmt='%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
print_welcome_message()
|
||||||
|
|
||||||
cfg = config.load()
|
cfg = config.load()
|
||||||
levels = cfg.levels
|
levels = cfg.levels
|
||||||
db = database.Database(cfg.database_path)
|
db = database.Database(cfg.database_path)
|
||||||
@@ -30,21 +45,26 @@ def run():
|
|||||||
username=cfg.username,
|
username=cfg.username,
|
||||||
password=cfg.password,
|
password=cfg.password,
|
||||||
user_agent=USER_AGENT)
|
user_agent=USER_AGENT)
|
||||||
subreddit = reddit.subreddit(cfg.subreddit)
|
logging.info('Connected to Reddit as %s', reddit.user.me())
|
||||||
|
if not reddit.read_only:
|
||||||
|
logging.info('Has write access to Reddit')
|
||||||
|
else:
|
||||||
|
logging.info('Has read-only access to Reddit')
|
||||||
|
|
||||||
print_level(0, f'Connected to Reddit as {reddit.user.me()}')
|
subreddit = reddit.subreddit(cfg.subreddit)
|
||||||
print_level(1, f'Read-only? {reddit.read_only}')
|
logging.info('Watching subreddit %s', subreddit.title)
|
||||||
print_level(0, f'Watching subreddit {subreddit.title}')
|
if subreddit.moderator(redditor=reddit.user.me()):
|
||||||
is_mod = bool(subreddit.moderator(redditor=reddit.user.me()))
|
logging.info('Is moderator for monitored subreddit')
|
||||||
print_level(1, f'Is mod? {is_mod}')
|
else:
|
||||||
|
logging.warning('Is NOT moderator for monitored subreddit')
|
||||||
|
|
||||||
monitor_comments(subreddit, db, levels)
|
monitor_comments(subreddit, db, levels)
|
||||||
# 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:
|
||||||
print('Unable to connect; attempting again....')
|
log.error('Unable to connect; attempting again....')
|
||||||
except prawcore.exceptions.ServerError as e:
|
except prawcore.exceptions.ServerError as e:
|
||||||
print('Lost connection to Reddit; attempting to reconnect....')
|
log.error('Lost connection to Reddit; attempting to reconnect....')
|
||||||
|
|
||||||
|
|
||||||
def monitor_comments(subreddit, db, levels):
|
def monitor_comments(subreddit, db, levels):
|
||||||
@@ -55,57 +75,70 @@ def monitor_comments(subreddit, db, levels):
|
|||||||
if comm is None:
|
if comm is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print_level(0, '\nFound comment')
|
logging.info('Received comment')
|
||||||
print_level(1, f'Comment text: "{comm.body}"')
|
# TODO more debug info about comment, eg author
|
||||||
|
logging.debug('Comment text: "%s"', comm.body)
|
||||||
|
# print_level(0, '\nFound comment')
|
||||||
|
# print_level(1, f'Comment text: "{comm.body}"')
|
||||||
|
|
||||||
if not marks_as_solved(comm):
|
if not marks_as_solved(comm):
|
||||||
print_level(1, 'Not a "![Ss]olved" comment')
|
logging.info('Comment does not mark issue as solved')
|
||||||
|
# print_level(1, 'Not a "![Ss]olved" comment')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
logging.info('Comment marks issues as solved')
|
||||||
|
|
||||||
if is_mod_comment(comm):
|
if is_mod_comment(comm):
|
||||||
print_level(1, 'Mod comment')
|
logging.info('Comment was submitted by mod')
|
||||||
|
# print_level(1, 'Mod comment')
|
||||||
elif not is_first_solution(comm):
|
elif not is_first_solution(comm):
|
||||||
# Skip this "!solved" comment and wait for the next
|
# Skip this "!solved" comment
|
||||||
print_level(1, 'Not the first solution')
|
logging.info('Comment is NOT the first to mark the issue as solved')
|
||||||
|
# print_level(1, 'Not the first solution')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print_level(1, 'This is the first solution found')
|
logging.info('Comment is the first to mark the issue as solved')
|
||||||
print_solution_info(comm)
|
# print_level(1, 'This is the first solution found')
|
||||||
|
log_solution_info(comm)
|
||||||
|
|
||||||
solver = find_solver(comm)
|
solver = find_solver(comm)
|
||||||
db.add_point(solver)
|
db.add_point(solver)
|
||||||
print_level(1, f'Added point for {solver.name}')
|
logging.info('Added point for user "%s"', solver.name)
|
||||||
|
# print_level(1, f'Added point for {solver.name}')
|
||||||
|
|
||||||
points = db.get_points(solver)
|
points = db.get_points(solver)
|
||||||
print_level(1, f'Total points for {solver.name}: {points}')
|
logging.info('Total points for user "%s": %d', solver.name, points)
|
||||||
|
# print_level(1, f'Total points for {solver.name}: {points}')
|
||||||
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(solver, points, level_info)
|
reply_body = reply.make(solver, points, level_info)
|
||||||
try:
|
try:
|
||||||
comm.reply(reply_body)
|
comm.reply(reply_body)
|
||||||
print_level(1, f'Replied to comment with: "{reply_body}"')
|
logging.info('Replied to the comment')
|
||||||
|
logging.debug('Reply body: %s', reply_body)
|
||||||
|
# print_level(1, f'Replied to comment with: "{reply_body}"')
|
||||||
except praw.exceptions.APIException as e:
|
except praw.exceptions.APIException as e:
|
||||||
print_level(1, 'Unable to reply to comment')
|
logging.error('Unable to reply to comment: %s', e)
|
||||||
print_level(2, f'{e}')
|
|
||||||
db.remove_point(solver)
|
db.remove_point(solver)
|
||||||
print_level(1, f'Removed point awarded to {solver.name}')
|
logging.error('Removed point that was just awarded to user "%s"', solver.name)
|
||||||
print_level(1, 'Skipping comment')
|
logging.error('Skipping comment')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Check if (non-mod) user flair should be updated to new level
|
# Check if (non-mod) user flair should be updated to new level
|
||||||
lvl = level_info.current
|
lvl = level_info.current
|
||||||
if lvl and lvl.points == points:
|
if lvl and lvl.points == points:
|
||||||
print_level(1, f'User reached level: {lvl.name}')
|
logging.info('User reached level: %s', lvl.name)
|
||||||
if not subreddit.moderator(redditor=solver):
|
if not subreddit.moderator(redditor=solver):
|
||||||
print_level(2, 'Setting flair')
|
# print_level(2, 'Setting flair')
|
||||||
print_level(3, f'Flair text: {lvl.name}')
|
logging.info('User is not mod; setting flair')
|
||||||
print_level(3, f'Flair template ID: {lvl.flair_template_id}')
|
logging.info('Flair text: %s', lvl.name)
|
||||||
|
logging.info('Flair template ID: %s', lvl.flair_template_id)
|
||||||
subreddit.flair.set(solver,
|
subreddit.flair.set(solver,
|
||||||
text=lvl.name,
|
text=lvl.name,
|
||||||
flair_template_id=lvl.flair_template_id)
|
flair_template_id=lvl.flair_template_id)
|
||||||
else:
|
else:
|
||||||
print_level(2, 'Solver is mod; don\'t alter flair')
|
logging.info('Solver is mod; don\'t alter flair')
|
||||||
|
|
||||||
|
|
||||||
### Reddit Comment Functions ###
|
### Reddit Comment Functions ###
|
||||||
@@ -154,20 +187,40 @@ def find_solver(solved_comment):
|
|||||||
return solved_comment.parent().author
|
return solved_comment.parent().author
|
||||||
|
|
||||||
|
|
||||||
### Debugging & Logging ###
|
### Print Functions ###
|
||||||
|
|
||||||
|
|
||||||
|
def print_separator_line():
|
||||||
|
print('#' * 80)
|
||||||
|
|
||||||
|
|
||||||
|
def print_welcome_message():
|
||||||
|
print_separator_line()
|
||||||
|
print('\n*** Welcome to PointsBot! ***\n')
|
||||||
|
print_separator_line()
|
||||||
|
print('\nThis bot will monitor the subreddit specified in the '
|
||||||
|
'configuration file as long as this program is running.')
|
||||||
|
print('\nAny Reddit activity that occurs while this program is not running '
|
||||||
|
'will be missed. You can work around this by using features '
|
||||||
|
'mentioned in the README.')
|
||||||
|
print('\nThe output from this program can be referenced if any issues are '
|
||||||
|
'to occur, and the relevant error message or crash report can be '
|
||||||
|
'sent to the developer by reporting an issue on the Github page.')
|
||||||
|
print('\nFuture updates will hopefully resolve these issues, but for the '
|
||||||
|
"moment, this is what we've got to work with! :)\n")
|
||||||
|
|
||||||
|
|
||||||
def print_level(num_levels, string):
|
def print_level(num_levels, string):
|
||||||
print('\t' * num_levels + string)
|
print('\t' * num_levels + string)
|
||||||
|
|
||||||
|
|
||||||
def print_solution_info(comm):
|
def log_solution_info(comm):
|
||||||
print_level(1, 'Submission solved')
|
logging.info('Submission solved')
|
||||||
print_level(2, 'Solution comment:')
|
logging.debug('Solution comment:')
|
||||||
print_level(3, f'Author: {comm.parent().author.name}')
|
logging.debug('Author: %s', comm.parent().author.name)
|
||||||
print_level(3, f'Body: {comm.parent().body}')
|
logging.debug('Body: %s', comm.parent().body)
|
||||||
print_level(2, '"Solved" comment:')
|
logging.debug('"Solved" comment:')
|
||||||
print_level(3, f'Author: {comm.author.name}')
|
logging.debug('Author: %s', comm.author.name)
|
||||||
print_level(3, f'Body: {comm.body}')
|
logging.debug('Body: %s', comm.body)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -115,19 +115,39 @@ def interactive_config(dest):
|
|||||||
|
|
||||||
print('#' * 80 + '\nCONFIGURING THE BOT\n' + '#' * 80)
|
print('#' * 80 + '\nCONFIGURING THE BOT\n' + '#' * 80)
|
||||||
print('\nType a value for each field, then press enter.')
|
print('\nType a value for each field, then press enter.')
|
||||||
print('\nIf the field is specified as optional, leave blank to skip.\n')
|
print('\nIf a field is specified as optional, then you can skip it by just '
|
||||||
|
'pressing enter.\n')
|
||||||
|
|
||||||
configvals['core']['subreddit'] = input('subreddit? ')
|
configvals['core']['subreddit'] = input('name of subreddit to monitor? ')
|
||||||
print()
|
print()
|
||||||
configvals['filepaths']['database'] = input('database filename? (optional) ')
|
configvals['filepaths']['database'] = input('database filename? (optional) ')
|
||||||
print()
|
print('\n*** Bot account details ***\n')
|
||||||
configvals['credentials']['client_id'] = input('client_id? ')
|
configvals['credentials']['client_id'] = input('client_id? ')
|
||||||
configvals['credentials']['client_secret'] = input('client_secret? ')
|
configvals['credentials']['client_secret'] = input('client_secret? ')
|
||||||
configvals['credentials']['username'] = input('username? ')
|
configvals['credentials']['username'] = input('bot username? ')
|
||||||
configvals['credentials']['password'] = input('password? ')
|
configvals['credentials']['password'] = input('bot password? ')
|
||||||
|
|
||||||
add_another_level = True
|
print('\n*** Flair Levels ***\n')
|
||||||
while add_another_level:
|
print('These fields will determine the different levels that your '
|
||||||
|
'subreddit users can achieve by earning points.')
|
||||||
|
print('\nFor each level, you should provide...')
|
||||||
|
print("\t- Level name: the text that appears in the user's flair")
|
||||||
|
print('\t- Level points: the number of points needed to reach the level')
|
||||||
|
print('\t- Flair template ID: (optional) the flair template ID in your')
|
||||||
|
print('\t subreddit to be used for this level flair')
|
||||||
|
print('\nThese may be provided in any order; the bot will sort them later.')
|
||||||
|
print('\nDo not provide more than one level with the same number of points.')
|
||||||
|
print('\nNote that at the moment, providing a level points value of zero '
|
||||||
|
'will not set a default flair, because users must solve at least one '
|
||||||
|
'issue before the bot will keep track of their points and set their '
|
||||||
|
'flair for the first time.')
|
||||||
|
print('\nFor any more questions, please refer to the README on the Github '
|
||||||
|
'page.')
|
||||||
|
|
||||||
|
# add_another_level = True
|
||||||
|
response = 'y'
|
||||||
|
while response.lower().startswith('y'):
|
||||||
|
print('\n*** Adding a level ***')
|
||||||
level = {}
|
level = {}
|
||||||
level['name'] = input('\nLevel name? ')
|
level['name'] = input('\nLevel name? ')
|
||||||
level['points'] = int(input('Level points? '))
|
level['points'] = int(input('Level points? '))
|
||||||
@@ -135,7 +155,7 @@ def interactive_config(dest):
|
|||||||
configvals['levels'].append(level)
|
configvals['levels'].append(level)
|
||||||
|
|
||||||
response = input('\nAdd another level? (y/n) ')
|
response = input('\nAdd another level? (y/n) ')
|
||||||
add_another_level = response.lower().startswith('y')
|
# add_another_level = response.lower().startswith('y')
|
||||||
|
|
||||||
with open(dest, 'w') as f:
|
with open(dest, 'w') as f:
|
||||||
toml.dump(configvals, f)
|
toml.dump(configvals, f)
|
||||||
|
|||||||
3
pyinstaller-hooks/hook-praw.py
Normal file
3
pyinstaller-hooks/hook-praw.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from PyInstaller.utils.hooks import collect_data_files
|
||||||
|
|
||||||
|
datas = collect_data_files('praw')
|
||||||
Reference in New Issue
Block a user