Added config options & basic single-file executable
This commit is contained in:
13
CHANGELOG.md
Normal file
13
CHANGELOG.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 2020/05/10
|
||||||
|
|
||||||
|
Features:
|
||||||
|
1. Adding basic initial logging to a file
|
||||||
|
|
||||||
|
Fixes: N/A
|
||||||
|
|
||||||
|
Miscellaneous:
|
||||||
|
1. Moved feedback & scoreboard links for bot reply into configuration
|
||||||
|
2. Changed program entry point to `PointsBot.py`
|
||||||
|
3. Added ability to freeze the app as a simple executable
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
"""Dummy file used for either runnning or freezing the bot."""
|
"""Entry point used for either runnning or freezing the bot."""
|
||||||
import pointsbot
|
import pointsbot
|
||||||
|
|
||||||
pointsbot.run()
|
pointsbot.run()
|
||||||
|
|||||||
@@ -19,19 +19,15 @@ pyz = PYZ(a.pure, a.zipped_data,
|
|||||||
cipher=block_cipher)
|
cipher=block_cipher)
|
||||||
exe = EXE(pyz,
|
exe = EXE(pyz,
|
||||||
a.scripts,
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
[],
|
[],
|
||||||
exclude_binaries=True,
|
|
||||||
name='PointsBot',
|
name='PointsBot',
|
||||||
debug=False,
|
debug=False,
|
||||||
bootloader_ignore_signals=False,
|
bootloader_ignore_signals=False,
|
||||||
strip=False,
|
strip=False,
|
||||||
upx=True,
|
upx=True,
|
||||||
console=True )
|
|
||||||
coll = COLLECT(exe,
|
|
||||||
a.binaries,
|
|
||||||
a.zipfiles,
|
|
||||||
a.datas,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
upx_exclude=[],
|
||||||
name='PointsBot')
|
runtime_tmpdir=None,
|
||||||
|
console=True )
|
||||||
|
|||||||
116
README.md
116
README.md
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
* [Description](#description)
|
* [Description](#description)
|
||||||
* [Installation](#installation)
|
* [Installation](#installation)
|
||||||
|
* [Prerequisites & Configuration](#prerequisites--configuration)
|
||||||
* [Usage](#usage)
|
* [Usage](#usage)
|
||||||
* [Setup](#setup)
|
|
||||||
* [Terms of Use for a bot for Reddit](#terms-of-use-for-a-bot-for-reddit)
|
* [Terms of Use for a bot for Reddit](#terms-of-use-for-a-bot-for-reddit)
|
||||||
* [License](#license)
|
* [License](#license)
|
||||||
|
|
||||||
@@ -41,8 +41,19 @@ awarded for each submission.
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Requirements:
|
### Basic Installation
|
||||||
|
|
||||||
|
These are the instructions for simply using the bot without needing to edit the
|
||||||
|
code. These instructions are best suited for users with less technical
|
||||||
|
experience.
|
||||||
|
|
||||||
|
Go the the [releases page](https://github.com/cur33/PointsBot/releases) for this
|
||||||
|
project, then download and unzip the latest release. Make sure you pick the
|
||||||
|
release best suited for your machine & operating system.
|
||||||
|
|
||||||
|
### Advanced Installation
|
||||||
|
|
||||||
|
Requirements:
|
||||||
* [Python 3](https://www.python.org/downloads/) (specifically version 3.7 or greater)
|
* [Python 3](https://www.python.org/downloads/) (specifically version 3.7 or greater)
|
||||||
* pip (should be installed automatically with Python)
|
* pip (should be installed automatically with Python)
|
||||||
* [pipenv](https://pipenv.readthedocs.io/en/latest/)
|
* [pipenv](https://pipenv.readthedocs.io/en/latest/)
|
||||||
@@ -60,39 +71,7 @@ To uninstall (i.e. delete the project's virtual environment and the installed
|
|||||||
python packages), navigate to the project root directory and instead run
|
python packages), navigate to the project root directory and instead run
|
||||||
`pipenv --rm`.
|
`pipenv --rm`.
|
||||||
|
|
||||||
## Usage
|
## Prerequisites & Configuration
|
||||||
|
|
||||||
Once you have followed the instructions in the [Setup](#setup) section below,
|
|
||||||
the simplest way to run the bot is to navigate to the project root directory and
|
|
||||||
run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pipenv run python -m pointsbot
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
### Configuration file
|
|
||||||
|
|
||||||
The bot stores any necessary data, including configuration, under the
|
|
||||||
`.pointsbot` directory in your home directory.
|
|
||||||
|
|
||||||
If this is your first time running the bot, you will need to copy
|
|
||||||
`pointsbot.sample.toml` to a new file called `.pointsbot\pointsbot.toml`,
|
|
||||||
located in the `.pointsbot` directory mentioned above. Any instances of
|
|
||||||
the word "REDACTED" should be replaced with the appropriate values; other values
|
|
||||||
should work as-is, but can be changed as needed.
|
|
||||||
|
|
||||||
This is because the config file can contain sensitive information, and
|
|
||||||
maintaining only sample versions of these files helps developers to avoid
|
|
||||||
accidentally uploading that sensitive information to a public (or even private)
|
|
||||||
code repository.
|
|
||||||
|
|
||||||
More information on the specific config options can be found in the comments in
|
|
||||||
the sample config file.
|
|
||||||
|
|
||||||
You shouldn't have to worry about it, but if you need it, information on the
|
|
||||||
TOML syntax used for the file can be found on [Github](https://github.com/toml-lang/toml).
|
|
||||||
|
|
||||||
### Bot account
|
### Bot account
|
||||||
|
|
||||||
@@ -130,6 +109,73 @@ Some of the bot's behaviors, e.g. altering redditor flairs, require moderator
|
|||||||
permissions. It should require just the "Flair" and "Posts" permissions and
|
permissions. It should require just the "Flair" and "Posts" permissions and
|
||||||
perhaps the "Access" permission, so you don't need to grant it full permissions.
|
perhaps the "Access" permission, so you don't need to grant it full permissions.
|
||||||
|
|
||||||
|
### Configuration file
|
||||||
|
|
||||||
|
The bot will store its configuration file in a `.pointsbot` directory under the
|
||||||
|
home directory for your user on your machine. By default, this directory is also
|
||||||
|
where it will store its database and log files, but you can alter this behavior
|
||||||
|
by specifying other locations for those in the configuration process.
|
||||||
|
|
||||||
|
If this is your first time running the bot, then it will fail to detect a
|
||||||
|
configuration file and will prompt you for the necessary fields. This includes
|
||||||
|
the credentials for the Reddit account and Reddit app which you created for the
|
||||||
|
bot in the [Prerequisites & Configuration](#prerequisites--configuration) step
|
||||||
|
above. The bot will create the configuration file and save this information for
|
||||||
|
future use.
|
||||||
|
|
||||||
|
At the moment, there is not a good way to edit the existing configuration unless
|
||||||
|
you want to edit the configuration file yourself (see next paragraph). This is
|
||||||
|
the recommended process for editing the configuration until this feature is
|
||||||
|
added:
|
||||||
|
1. Go to your home directory and open the `.pointsbot` directory.
|
||||||
|
2. Either rename the `pointsbot.toml` file to something else, or move it to a
|
||||||
|
different directory.
|
||||||
|
3. Open that file in a text editor (if you haven't installed one, you can use
|
||||||
|
the default text editor for your operating system, like Notepad on Windows).
|
||||||
|
4. Run the bot and walk through the configuration process again, copying and
|
||||||
|
pasting any unchanged values from the original configuration file.
|
||||||
|
5. Once you are finished, you can either keep the old configuration file for a
|
||||||
|
while just in case, or you can delete it.
|
||||||
|
|
||||||
|
If you'd prefer, you can instead create and edit the configuration file
|
||||||
|
yourself. You will need to copy `pointsbot.sample.toml` to a new file called
|
||||||
|
`pointsbot.toml` located in the `.pointsbot` directory mentioned above. Any
|
||||||
|
instances of the word "REDACTED" should be replaced with the appropriate values;
|
||||||
|
other values should work as-is, but can be changed as needed.
|
||||||
|
|
||||||
|
This is because the config file can contain sensitive information, and
|
||||||
|
maintaining only sample versions of these files in this repository helps
|
||||||
|
developers to avoid accidentally uploading that sensitive information to a
|
||||||
|
public (or even private) code repository.
|
||||||
|
|
||||||
|
More information on the specific config options can be found in the comments in
|
||||||
|
the sample config file.
|
||||||
|
|
||||||
|
You shouldn't have to worry about it, but if you need it, information on the
|
||||||
|
TOML syntax used for the file can be found on
|
||||||
|
[TOML's Github page](https://github.com/toml-lang/toml).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
Follow these instructions if you downloaded the bot from the releases page in
|
||||||
|
the [Installation](#installation) step above.
|
||||||
|
|
||||||
|
In the unzipped folder, double-click on the `PointsBot.exe` file. It will open a
|
||||||
|
command prompt that will ask you for any additional information it may require.
|
||||||
|
You will *not* need any knowledge of the command prompt for your operating
|
||||||
|
system to interact with the bot.
|
||||||
|
|
||||||
|
### Advanced Usage
|
||||||
|
|
||||||
|
The simplest way to run the bot is to navigate to the project root directory and
|
||||||
|
run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pipenv run python PointsBot.py
|
||||||
|
```
|
||||||
|
|
||||||
## Terms of use for a bot for Reddit
|
## Terms of use for a bot for Reddit
|
||||||
|
|
||||||
Since this is an open-source, unmonetized program, it should be considered
|
Since this is an open-source, unmonetized program, it should be considered
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
|
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
|
||||||
REM )
|
REM )
|
||||||
|
|
||||||
REM --add-data "%pipenvdir%\Lib\site-packages\praw\praw.ini;site-packages\praw\praw.ini" ^
|
REM --add-data "%pipenvdir%\Lib\site-packages\praw\praw.ini;site-packages\praw\praw.ini" ^
|
||||||
|
|
||||||
REM --noconfirm ^
|
REM using the --noconfirm option sometimes causes issues when rebuilding
|
||||||
|
REM (ie when it tries to delete the previous dist directory)
|
||||||
pyinstaller ^
|
pyinstaller ^
|
||||||
--onedir ^
|
--onefile ^
|
||||||
--additional-hooks-dir .\pyinstaller-hooks\ ^
|
--additional-hooks-dir .\pyinstaller-hooks\ ^
|
||||||
PointsBot.py
|
PointsBot.py
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
from . import bot
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
bot.run()
|
|
||||||
@@ -13,9 +13,9 @@ from . import config, database, level, reply
|
|||||||
USER_AGENT = 'PointsBot (by u/GlipGlorp7)'
|
USER_AGENT = 'PointsBot (by u/GlipGlorp7)'
|
||||||
|
|
||||||
# TODO put this in config
|
# TODO put this in config
|
||||||
LOG_FILEPATH = os.path.abspath(os.path.join(os.path.expanduser('~'),
|
# LOG_FILEPATH = os.path.abspath(os.path.join(os.path.expanduser('~'),
|
||||||
'.pointsbot',
|
# '.pointsbot',
|
||||||
'pointsbot.log.txt'))
|
# 'pointsbot.log'))
|
||||||
|
|
||||||
# The pattern that determines whether a post is marked as solved
|
# The pattern that determines whether a post is marked as solved
|
||||||
# Could also use re.IGNORECASE flag instead
|
# Could also use re.IGNORECASE flag instead
|
||||||
@@ -26,14 +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()
|
print_welcome_message()
|
||||||
|
|
||||||
cfg = config.load()
|
cfg = config.load()
|
||||||
|
logging.basicConfig(filename=cfg.log_path,
|
||||||
|
level=logging.DEBUG,
|
||||||
|
format='%(asctime)s %(levelname)s:%(module)s: %(message)s',
|
||||||
|
datefmt='%Y-%m-%d %H:%M:%S')
|
||||||
levels = cfg.levels
|
levels = cfg.levels
|
||||||
db = database.Database(cfg.database_path)
|
db = database.Database(cfg.database_path)
|
||||||
|
|
||||||
@@ -78,46 +77,43 @@ 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 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):
|
||||||
logging.info('Comment does not mark issue as solved')
|
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')
|
logging.info('Comment marks issues as solved')
|
||||||
|
|
||||||
if is_mod_comment(comm):
|
if is_mod_comment(comm):
|
||||||
logging.info('Comment was submitted by mod')
|
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
|
# Skip this "!solved" comment
|
||||||
logging.info('Comment is NOT the first to mark the issue as solved')
|
logging.info('Comment is NOT the first to mark the issue as solved')
|
||||||
# print_level(1, 'Not the first solution')
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logging.info('Comment is the first to mark the issue as solved')
|
logging.info('Comment is the first to mark the issue as solved')
|
||||||
# print_level(1, 'This is the first solution found')
|
|
||||||
log_solution_info(comm)
|
log_solution_info(comm)
|
||||||
|
|
||||||
solver = find_solver(comm)
|
solver = find_solver(comm)
|
||||||
db.add_point(solver)
|
db.add_point(solver)
|
||||||
logging.info('Added point for user "%s"', 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)
|
||||||
logging.info('Total points for user "%s": %d', 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,
|
||||||
|
feedback_url=cfg.feedback_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)
|
||||||
# print_level(1, f'Replied to comment with: "{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)
|
||||||
@@ -130,7 +126,6 @@ def monitor_comments(subreddit, db, levels):
|
|||||||
if lvl and lvl.points == points:
|
if lvl and lvl.points == points:
|
||||||
logging.info('User reached level: %s', 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')
|
|
||||||
logging.info('User is not mod; setting flair')
|
logging.info('User is not mod; setting flair')
|
||||||
logging.info('Flair text: %s', lvl.name)
|
logging.info('Flair text: %s', lvl.name)
|
||||||
logging.info('Flair template ID: %s', lvl.flair_template_id)
|
logging.info('Flair template ID: %s', lvl.flair_template_id)
|
||||||
@@ -208,10 +203,7 @@ def print_welcome_message():
|
|||||||
'sent to the developer by reporting an issue on the Github page.')
|
'sent to the developer by reporting an issue on the Github page.')
|
||||||
print('\nFuture updates will hopefully resolve these issues, but for the '
|
print('\nFuture updates will hopefully resolve these issues, but for the '
|
||||||
"moment, this is what we've got to work with! :)\n")
|
"moment, this is what we've got to work with! :)\n")
|
||||||
|
print_separator_line()
|
||||||
|
|
||||||
def print_level(num_levels, string):
|
|
||||||
print('\t' * num_levels + string)
|
|
||||||
|
|
||||||
|
|
||||||
def log_solution_info(comm):
|
def log_solution_info(comm):
|
||||||
|
|||||||
@@ -13,30 +13,57 @@ DATADIR = os.path.join(os.path.expanduser('~'), '.pointsbot')
|
|||||||
CONFIGPATH = os.path.join(DATADIR, 'pointsbot.toml')
|
CONFIGPATH = os.path.join(DATADIR, 'pointsbot.toml')
|
||||||
|
|
||||||
# Path to the sample config file
|
# Path to the sample config file
|
||||||
|
# Unused for now
|
||||||
SAMPLEPATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
SAMPLEPATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||||
'..',
|
'..',
|
||||||
'pointsbot.sample.toml'))
|
'pointsbot.sample.toml'))
|
||||||
|
|
||||||
|
### Primary Functions ###
|
||||||
|
|
||||||
|
|
||||||
|
def load(filepath=CONFIGPATH):
|
||||||
|
# Prompt user for config values if file doesn't exist
|
||||||
|
if not os.path.exists(filepath):
|
||||||
|
datadir = os.path.dirname(filepath)
|
||||||
|
if not os.path.exists(datadir):
|
||||||
|
os.makedirs(datadir)
|
||||||
|
interactive_config(filepath)
|
||||||
|
|
||||||
|
return Config.from_toml(filepath)
|
||||||
|
|
||||||
|
|
||||||
### Classes ###
|
### Classes ###
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
|
|
||||||
# Default config vals
|
# Default config vals
|
||||||
DEFAULT_DBNAME = 'pointsbot.db'
|
DEFAULT_DB_NAME = 'pointsbot.db'
|
||||||
|
DEFAULT_LOG_NAME = 'pointsbot.log'
|
||||||
|
|
||||||
def __init__(self, filepath, subreddit, client_id, client_secret, username,
|
def __init__(self, filepath, subreddit, client_id, client_secret, username,
|
||||||
password, levels, database_path=None):
|
password, levels, database_path=None, log_path=None,
|
||||||
|
feedback_url=None, scoreboard_url=None):
|
||||||
self._filepath = filepath
|
self._filepath = filepath
|
||||||
self._dirname = os.path.dirname(filepath)
|
self._dirname = os.path.dirname(filepath)
|
||||||
|
|
||||||
|
if not log_path:
|
||||||
|
log_path = os.path.join(self._dirname, self.DEFAULT_LOG_NAME)
|
||||||
|
elif os.path.isdir(log_path):
|
||||||
|
log_path = os.path.join(log_path, self.DEFAULT_LOG_NAME)
|
||||||
|
self.log_path = log_path
|
||||||
|
|
||||||
|
# TODO init logging here so it can be used immediately?
|
||||||
|
|
||||||
if not database_path:
|
if not database_path:
|
||||||
database_path = os.path.join(self._dirname, self.DEFAULT_DBNAME)
|
database_path = os.path.join(self._dirname, self.DEFAULT_DB_NAME)
|
||||||
elif os.path.isdir(database_path):
|
elif os.path.isdir(database_path):
|
||||||
database_path = os.path.join(database_path, self.DEFAULT_DBNAME)
|
database_path = os.path.join(database_path, self.DEFAULT_DB_NAME)
|
||||||
self.database_path = database_path
|
self.database_path = database_path
|
||||||
|
|
||||||
self.subreddit = subreddit
|
self.subreddit = subreddit
|
||||||
|
self.feedback_url = feedback_url
|
||||||
|
self.scoreboard_url = scoreboard_url
|
||||||
|
|
||||||
self.client_id = client_id
|
self.client_id = client_id
|
||||||
self.client_secret = client_secret
|
self.client_secret = client_secret
|
||||||
@@ -62,6 +89,10 @@ class Config:
|
|||||||
if dbpath:
|
if dbpath:
|
||||||
dbpath = os.path.abspath(os.path.expandvars(os.path.expanduser(dbpath)))
|
dbpath = os.path.abspath(os.path.expandvars(os.path.expanduser(dbpath)))
|
||||||
|
|
||||||
|
logpath = obj['filepaths']['log']
|
||||||
|
if logpath:
|
||||||
|
logpath = os.path.abspath(os.path.expandvars(os.path.expanduser(logpath)))
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
filepath,
|
filepath,
|
||||||
obj['core']['subreddit'],
|
obj['core']['subreddit'],
|
||||||
@@ -71,12 +102,15 @@ class Config:
|
|||||||
obj['credentials']['password'],
|
obj['credentials']['password'],
|
||||||
levels,
|
levels,
|
||||||
database_path=dbpath,
|
database_path=dbpath,
|
||||||
|
log_path=logpath,
|
||||||
|
feedback_url=obj['links']['feedback'],
|
||||||
|
scoreboard_url=obj['links']['scoreboard'],
|
||||||
)
|
)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
obj = deepcopy(vars(self))
|
obj = deepcopy(vars(self))
|
||||||
orig_levels = obj['levels']
|
orig_levels, obj['levels'] = obj['levels'], []
|
||||||
obj['levels'] = []
|
# obj['levels'] = []
|
||||||
for level in orig_levels:
|
for level in orig_levels:
|
||||||
obj['levels'].append({
|
obj['levels'].append({
|
||||||
'name': level.name,
|
'name': level.name,
|
||||||
@@ -88,20 +122,6 @@ class Config:
|
|||||||
toml.dump(obj, f)
|
toml.dump(obj, f)
|
||||||
|
|
||||||
|
|
||||||
### Functions ###
|
|
||||||
|
|
||||||
|
|
||||||
def load(filepath=CONFIGPATH):
|
|
||||||
# Prompt user for config values if file doesn't exist
|
|
||||||
if not os.path.exists(filepath):
|
|
||||||
datadir = os.path.dirname(filepath)
|
|
||||||
if not os.path.exists(datadir):
|
|
||||||
os.makedirs(datadir)
|
|
||||||
interactive_config(filepath)
|
|
||||||
|
|
||||||
return Config.from_toml(filepath)
|
|
||||||
|
|
||||||
|
|
||||||
### Interactive Config Editing ###
|
### Interactive Config Editing ###
|
||||||
|
|
||||||
|
|
||||||
@@ -113,14 +133,25 @@ def interactive_config(dest):
|
|||||||
'levels': [],
|
'levels': [],
|
||||||
}
|
}
|
||||||
|
|
||||||
print('#' * 80 + '\nCONFIGURING THE BOT\n' + '#' * 80)
|
print('\n' + ('#' * 80) + '\nCONFIGURING THE BOT\n' + ('#' * 80) + '\n')
|
||||||
print('\nType a value for each field, then press enter.')
|
print('Type a value for each field, then press enter.')
|
||||||
print('\nIf a field is specified as optional, then you can skip it by just '
|
print('\nIf a field is specified as optional, then you can skip it by just '
|
||||||
'pressing enter.\n')
|
'pressing enter.')
|
||||||
|
print("\nIt is recommended that you skip any fields that you aren't sure "
|
||||||
|
'about')
|
||||||
|
|
||||||
|
print('\n*** Core Configuration ***\n')
|
||||||
configvals['core']['subreddit'] = input('name of subreddit to monitor? ')
|
configvals['core']['subreddit'] = input('name of subreddit to monitor? ')
|
||||||
print()
|
print()
|
||||||
configvals['filepaths']['database'] = input('database filename? (optional) ')
|
configvals['filepaths']['database'] = input('database filepath? (optional) ')
|
||||||
|
configvals['filepaths']['log'] = input('log filepath? (optional) ')
|
||||||
|
|
||||||
|
print('\n*** Website Links ***\n')
|
||||||
|
print('These values should only be provided if you have valid URLs for '
|
||||||
|
'websites that provide these services for your subreddit.\n')
|
||||||
|
configvals['links']['feedback'] = input('feedback webpage URL? (optional) ')
|
||||||
|
configvals['links']['scoreboard'] = input('scoreboard webpage URL? (optional) ')
|
||||||
|
|
||||||
print('\n*** Bot account details ***\n')
|
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? ')
|
||||||
@@ -144,18 +175,16 @@ def interactive_config(dest):
|
|||||||
print('\nFor any more questions, please refer to the README on the Github '
|
print('\nFor any more questions, please refer to the README on the Github '
|
||||||
'page.')
|
'page.')
|
||||||
|
|
||||||
# add_another_level = True
|
|
||||||
response = 'y'
|
response = 'y'
|
||||||
while response.lower().startswith('y'):
|
while response.lower().startswith('y'):
|
||||||
print('\n*** Adding a level ***')
|
print('\n*** Adding a level ***\n')
|
||||||
level = {}
|
level = {}
|
||||||
level['name'] = input('\nLevel name? ')
|
level['name'] = input('Level name? ')
|
||||||
level['points'] = int(input('Level points? '))
|
level['points'] = int(input('Level points? '))
|
||||||
level['flair_template_id'] = input('Flair template ID? (optional) ')
|
level['flair_template_id'] = input('Flair template ID? (optional) ')
|
||||||
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')
|
|
||||||
|
|
||||||
with open(dest, 'w') as f:
|
with open(dest, 'w') as f:
|
||||||
toml.dump(configvals, f)
|
toml.dump(configvals, f)
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ FILLED_SYMBOL = '\u25AE' # A small filled box character
|
|||||||
EMPTY_SYMBOL = '\u25AF' # A same-sized empty box character
|
EMPTY_SYMBOL = '\u25AF' # A same-sized empty box character
|
||||||
|
|
||||||
# Number of "excess" points should be greater than max level points
|
# Number of "excess" points should be greater than max level points
|
||||||
EXCESS_POINTS = 100 # TODO move this to level?
|
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
|
||||||
|
|
||||||
### Main Functions ###
|
### Main Functions ###
|
||||||
|
|
||||||
|
|
||||||
def make(redditor, points, level_info):
|
def make(redditor, points, level_info, feedback_url=None, scoreboard_url=None):
|
||||||
paras = [header()]
|
paras = [header()]
|
||||||
|
|
||||||
if points == 1:
|
if points == 1:
|
||||||
@@ -45,7 +45,7 @@ def make(redditor, points, level_info):
|
|||||||
|
|
||||||
paras.append(points_status(redditor, points, level_info))
|
paras.append(points_status(redditor, points, level_info))
|
||||||
paras.append(divider())
|
paras.append(divider())
|
||||||
paras.append(footer())
|
paras.append(footer(feedback_url=feedback_url, scoreboard_url=scoreboard_url))
|
||||||
return '\n\n'.join(paras)
|
return '\n\n'.join(paras)
|
||||||
|
|
||||||
|
|
||||||
@@ -130,10 +130,21 @@ def divider():
|
|||||||
return '***'
|
return '***'
|
||||||
|
|
||||||
|
|
||||||
def footer():
|
def footer(feedback_url=None, scoreboard_url=None):
|
||||||
return ('^(Bot maintained by GlipGlorp7 '
|
footer_sections = ['Bot maintained by GlipGlorp7']
|
||||||
'| [Scoreboard](https://points.minecrafthelp.co.uk) '
|
if scoreboard_url:
|
||||||
'| [Feedback](https://forms.gle/m94aGjFQwGopqQ836) '
|
# https://points.minecrafthelp.co.uk
|
||||||
'| [Source Code](https://github.com/cur33/PointsBot))')
|
footer_sections.append(f'[Scoreboard]({scoreboard_url})')
|
||||||
|
if feedback_url:
|
||||||
|
# https://forms.gle/m94aGjFQwGopqQ836
|
||||||
|
footer_sections.append(f'[Feedback]({feedback_url})')
|
||||||
|
footer_sections.append('[Source Code](https://github.com/cur33/PointsBot)')
|
||||||
|
|
||||||
|
return '^(' + ' | '.join(footer_sections) + ')'
|
||||||
|
|
||||||
|
# return ('^(Bot maintained by GlipGlorp7 '
|
||||||
|
# '| [Scoreboard](https://points.minecrafthelp.co.uk) '
|
||||||
|
# '| [Feedback](https://forms.gle/m94aGjFQwGopqQ836) '
|
||||||
|
# '| [Source Code](https://github.com/cur33/PointsBot))')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user