Compare commits
	
		
			10 Commits
		
	
	
		
			d1416d3706
			...
			8c19088640
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 8c19088640 | |||
| 5316f7e205 | |||
| 31426fd851 | |||
| 8d5a3cda42 | |||
| e237e7f885 | |||
| 80037593a9 | |||
| d484a0a363 | |||
| 348e674f74 | |||
| ca9aa6d79d | |||
| ae42a052de | 
							
								
								
									
										23
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								README.md
									
									
									
									
									
								
							| @ -265,6 +265,16 @@ python -m flask refresh-milestones | |||||||
| ``` | ``` | ||||||
| Running this command shouldn't reset progress on milestones that are already in the database | Running this command shouldn't reset progress on milestones that are already in the database | ||||||
| and are still in the redeems file. | and are still in the redeems file. | ||||||
|  | #### reset-milestone | ||||||
|  | Resets progress on a milestone, but only if the milestone had been completed. | ||||||
|  | ```bash | ||||||
|  | python -m flask reset-milestone milestone | ||||||
|  | ``` | ||||||
|  | #### hard-reset-milestone | ||||||
|  | Resets progress on a milestone, regardless of completion status. | ||||||
|  | ```bash | ||||||
|  | python -m flask hard-reset-milestone milestone | ||||||
|  | ``` | ||||||
| ## Configuration files | ## Configuration files | ||||||
| Configuration files should be in the instance folder. For folder installation of tlapbot, | Configuration files should be in the instance folder. For folder installation of tlapbot, | ||||||
| that's `instance/` from the root of the Github repository. | that's `instance/` from the root of the Github repository. | ||||||
| @ -313,18 +323,21 @@ REDEEMS={ | |||||||
|     "lurk": {"price": 1, "type": "counter", "info": "Let us know you're going to lurk."}, |     "lurk": {"price": 1, "type": "counter", "info": "Let us know you're going to lurk."}, | ||||||
|     "react": {"price": 200, "type": "note", "info": "Attach link to a video for me to react to."}, |     "react": {"price": 200, "type": "note", "info": "Attach link to a video for me to react to."}, | ||||||
|     "request": {"price": 100, "type": "note", "info": "Request a level, gamemode, skin, etc."}, |     "request": {"price": 100, "type": "note", "info": "Request a level, gamemode, skin, etc."}, | ||||||
|     "go_nap": {"price": 1, "type": "milestone", "info": "Streamer will go nap when the goal is reached.", "goal": 1000}, |     "go_nap": {"goal": 1000, "type": "milestone", "info": "Streamer will go nap when the goal is reached."}, | ||||||
|     "inactive": {"price": 100, "type": "note", "info": "Example redeem that is inactive by default", "category": ["inactive"]} |     "inactive": {"price": 100, "type": "note", "info": "Example redeem that is inactive by default", "category": ["inactive"]} | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
| #### File format | #### File format | ||||||
| `redeems.py` is a config file with just a `REDEEMS` key, that assigns a dictionary of redeems to it. | `redeems.py` is a config file with just a `REDEEMS` key, that assigns a dictionary of redeems to it. | ||||||
| Each dictionary entry is a redeem, and the dictionary keys are strings that decides the chat command for the redeem. The value is another dictionary that needs to have entries for `"price"`, `"type"` and optionally `"info"` and `"category"`. If the `"type"` is `"milestone"`, there's an additional required `"goal"` field as well. | Each dictionary entry is a redeem, and the dictionary keys are strings that decides the chat command for the redeem. | ||||||
|  | The value is another dictionary that needs to have an entry for `"type"` and | ||||||
|  | an entry for `"price"` for non-milestones or `"goal"` for milestones. | ||||||
|  | Optionally, each redeem can also have `"info"` and `"category"` entries. | ||||||
| 
 | 
 | ||||||
| - `"price"` value should be an integer that decides how many points the redeem will cost. For milestone redeems, `"price"` determines minimum bid. | - `"price"` value should be an integer that decides how many points the redeem will cost. Milestone redeems don't use the `"price"` value, they instead need to have a `"goal"`. | ||||||
| - `"type"` value should be either `"list"`, `"counter"`, `"note"` or `"milestone"`. This decided the redeem's type, and whether it will show up as a counter at the top of the dashboard or as an entry in the "recent redeems" chart. |  | ||||||
| - `"info"` value should be a string that describes what the command does. It's optional, but I recommend writing one for all `"list"` and `"note"` redeems (so that chatters know that they should write a note). |  | ||||||
| - `"goal"` is a required field for milestone goals. It should be an integer, deciding the amount of points required to complete the milestone. | - `"goal"` is a required field for milestone goals. It should be an integer, deciding the amount of points required to complete the milestone. | ||||||
|  | - `"type"` value should be either `"list"`, `"counter"`, `"note"` or `"milestone"`. This decided the redeem's type, and whether it will show up as a counter at the top of the dashboard or as an entry in the "recent redeems" chart. | ||||||
|  | - `"info"` value should be a string that describes what the command does. It's optional, but I recommend writing one for all `"list"`, `"note"` and `"milestone"` redeems (so that chatters know what they're redeeming and whether they should leave a note). | ||||||
| - `"category"` is an optional list of strings, the categories the redeem is in. | - `"category"` is an optional list of strings, the categories the redeem is in. | ||||||
| If a category from the list is in `ACTIVE_CATEGORIES` from `config.py`, | If a category from the list is in `ACTIVE_CATEGORIES` from `config.py`, | ||||||
| then the redeem will be active. It will not be active if none of the categories | then the redeem will be active. It will not be active if none of the categories | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								setup.py
									
									
									
									
									
								
							| @ -2,7 +2,7 @@ from setuptools import find_packages, setup | |||||||
| 
 | 
 | ||||||
| setup( | setup( | ||||||
|     name='tlapbot', |     name='tlapbot', | ||||||
|     version='1.2.0', |     version='1.2.1', | ||||||
|     packages=find_packages(), |     packages=find_packages(), | ||||||
|     include_package_data=True, |     include_package_data=True, | ||||||
|     install_requires=[ |     install_requires=[ | ||||||
|  | |||||||
| @ -49,6 +49,8 @@ def create_app(test_config=None): | |||||||
|     app.cli.add_command(db.refresh_counters_command) |     app.cli.add_command(db.refresh_counters_command) | ||||||
|     app.cli.add_command(db.refresh_and_clear_command) |     app.cli.add_command(db.refresh_and_clear_command) | ||||||
|     app.cli.add_command(db.refresh_milestones_command) |     app.cli.add_command(db.refresh_milestones_command) | ||||||
|  |     app.cli.add_command(db.reset_milestone_command) | ||||||
|  |     app.cli.add_command(db.hard_reset_milestone_command) | ||||||
|      |      | ||||||
|     # scheduler job for giving points to users |     # scheduler job for giving points to users | ||||||
|     def proxy_job(): |     def proxy_job(): | ||||||
|  | |||||||
| @ -35,6 +35,8 @@ def insert_counters(db): | |||||||
|                 db.commit() |                 db.commit() | ||||||
|             except sqlite3.Error as e: |             except sqlite3.Error as e: | ||||||
|                 print("Failed inserting counters to db:", e.args[0]) |                 print("Failed inserting counters to db:", e.args[0]) | ||||||
|  |                 return False | ||||||
|  |     return True | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def init_db(): | def init_db(): | ||||||
| @ -43,7 +45,8 @@ def init_db(): | |||||||
|     with current_app.open_resource('schema.sql') as f: |     with current_app.open_resource('schema.sql') as f: | ||||||
|         db.executescript(f.read().decode('utf8')) |         db.executescript(f.read().decode('utf8')) | ||||||
| 
 | 
 | ||||||
|     insert_counters(db) |     if insert_counters(db): | ||||||
|  |         return True | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def clear_redeem_queue(): | def clear_redeem_queue(): | ||||||
| @ -59,6 +62,8 @@ def clear_redeem_queue(): | |||||||
|         db.commit() |         db.commit() | ||||||
|     except sqlite3.Error as e: |     except sqlite3.Error as e: | ||||||
|         print("Error occured deleting redeem queue:", e.args[0]) |         print("Error occured deleting redeem queue:", e.args[0]) | ||||||
|  |         return False | ||||||
|  |     return True | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def refresh_counters(): | def refresh_counters(): | ||||||
| @ -69,7 +74,9 @@ def refresh_counters(): | |||||||
|         db.commit() |         db.commit() | ||||||
|     except sqlite3.Error as e: |     except sqlite3.Error as e: | ||||||
|         print("Error occured deleting old counters:", e.args[0]) |         print("Error occured deleting old counters:", e.args[0]) | ||||||
|     insert_counters(db) |         return False | ||||||
|  |     if insert_counters(db): | ||||||
|  |         return True | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def refresh_milestones(): | def refresh_milestones(): | ||||||
| @ -90,6 +97,7 @@ def refresh_milestones(): | |||||||
|         db.commit() |         db.commit() | ||||||
|     except sqlite3.Error as e: |     except sqlite3.Error as e: | ||||||
|         print("Failed deleting old milestones from db:", e.args[0]) |         print("Failed deleting old milestones from db:", e.args[0]) | ||||||
|  |         return False | ||||||
| 
 | 
 | ||||||
|     # add new milestones |     # add new milestones | ||||||
|     try: |     try: | ||||||
| @ -114,26 +122,29 @@ def refresh_milestones(): | |||||||
|         db.commit() |         db.commit() | ||||||
|     except sqlite3.Error as e: |     except sqlite3.Error as e: | ||||||
|         print("Failed inserting milestones to db:", e.args[0]) |         print("Failed inserting milestones to db:", e.args[0]) | ||||||
|  |         return False | ||||||
|  |     return True | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def reset_milestone(milestone): | def reset_milestone(milestone): | ||||||
|     if not redeem_name in current_app.config['REDEEMS']: |     if not milestone in current_app.config['REDEEMS']: | ||||||
|         print(f"Failed resetting milestone, {milestone} not in redeems file.") |         print(f"Failed resetting milestone, {milestone} not in redeems file.") | ||||||
|         return None |         return False | ||||||
|     try: |     try: | ||||||
|  |         db = get_db() | ||||||
|         db.execute( |         db.execute( | ||||||
|             "DELETE FROM milestones WHERE name = ?", |             "DELETE FROM milestones WHERE name = ?", | ||||||
|             (milestone,) |             (milestone,) | ||||||
|         ) |         ) | ||||||
|         db.execute( |         db.execute( | ||||||
|             "INSERT INTO milestones(name, progress, goal) VALUES(?, ?, ?)", |             "INSERT INTO milestones(name, progress, goal, complete) VALUES(?, ?, ?, FALSE)", | ||||||
|             (milestone, 0, current_app.config['REDEEMS'][milestone]['goal']) |             (milestone, 0, current_app.config['REDEEMS'][milestone]['goal']) | ||||||
|         ) |         ) | ||||||
|         db.commit() |         db.commit() | ||||||
|         return True |         return True | ||||||
|     except Error as e: |     except sqlite3.Error as e: | ||||||
|         current_app.logger.error(f"Error occured adding a milestone: {e.args[0]}") |         current_app.logger.error(f"Error occured adding a milestone: {e.args[0]}") | ||||||
|         return None |         return False | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -141,16 +152,16 @@ def reset_milestone(milestone): | |||||||
| @with_appcontext | @with_appcontext | ||||||
| def init_db_command(): | def init_db_command(): | ||||||
|     """Clear the existing data and create new tables.""" |     """Clear the existing data and create new tables.""" | ||||||
|     init_db() |     if init_db(): | ||||||
|     click.echo('Initialized the database.') |         click.echo('Initialized the database.') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @click.command('clear-queue') | @click.command('clear-queue') | ||||||
| @with_appcontext | @with_appcontext | ||||||
| def clear_queue_command(): | def clear_queue_command(): | ||||||
|     """Remove all redeems from the redeem queue.""" |     """Remove all redeems from the redeem queue.""" | ||||||
|     clear_redeem_queue() |     if clear_redeem_queue(): | ||||||
|     click.echo('Cleared redeem queue.') |         click.echo('Cleared redeem queue.') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @click.command('refresh-counters') | @click.command('refresh-counters') | ||||||
| @ -158,17 +169,16 @@ def clear_queue_command(): | |||||||
| def refresh_counters_command(): | def refresh_counters_command(): | ||||||
|     """Refresh counters from current config file. |     """Refresh counters from current config file. | ||||||
|     (Remove old ones, add new ones.)""" |     (Remove old ones, add new ones.)""" | ||||||
|     refresh_counters() |     if refresh_counters(): | ||||||
|     click.echo('Counters refreshed.') |         click.echo('Counters refreshed.') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @click.command('clear-refresh') | @click.command('clear-refresh') | ||||||
| @with_appcontext | @with_appcontext | ||||||
| def refresh_and_clear_command(): | def refresh_and_clear_command(): | ||||||
|     """Refresh counters and clear queue.""" |     """Refresh counters and clear queue.""" | ||||||
|     refresh_counters() |     if refresh_counters() and clear_redeem_queue(): | ||||||
|     clear_redeem_queue() |         click.echo('Counters refreshed and queue cleared.') | ||||||
|     click.echo('Counters refreshed and queue cleared.') |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @click.command('refresh-milestones') | @click.command('refresh-milestones') | ||||||
| @ -176,31 +186,29 @@ def refresh_and_clear_command(): | |||||||
| def refresh_milestones_command(): | def refresh_milestones_command(): | ||||||
|     """Initialize all milestones from the redeems file,  |     """Initialize all milestones from the redeems file,  | ||||||
|     delete milestones not in redeem file.""" |     delete milestones not in redeem file.""" | ||||||
|     refresh_milestones() |     if refresh_milestones(): | ||||||
|     click.echo('Refreshed milestones.') |         click.echo('Refreshed milestones.') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @click.command('reset-milestone') | @click.command('reset-milestone') | ||||||
| @click.argument('milestone') | @click.argument('milestone') | ||||||
| def reset_milestone_command(milestone): | def reset_milestone_command(milestone): | ||||||
|     """Resets a completed milestone back to zero.""" |     """Resets a completed milestone back to zero.""" | ||||||
|     if milestone_complete(milestone): |     if milestone_complete(get_db(), milestone): | ||||||
|         if reset_milestone(milestone): |         if reset_milestone(milestone): | ||||||
|             click.echo(f"Reset milestone {milestone}.") |             click.echo(f"Reset milestone {milestone}.") | ||||||
|         else: |  | ||||||
|             click.echo(f"Resetting milestone {milestone} failed.") |  | ||||||
|     else: |     else: | ||||||
|         click.echo(f"Could not reset milestone {milestone}, milestone not completed.") |         click.echo(f"Could not reset milestone {milestone}, milestone not completed.") | ||||||
|         click.echo("(You can hard-reset-milestone if you really want to reset it.)") |         click.echo("(You can hard-reset-milestone if you really want to reset it.)") | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| @click.command('hard-reset-milestone') | @click.command('hard-reset-milestone') | ||||||
| @click.argument('milestone') | @click.argument('milestone') | ||||||
| def hard_reset_milestone_command(milestone): | def hard_reset_milestone_command(milestone): | ||||||
|     """Resets any milestone back to zero.""" |     """Resets any milestone back to zero.""" | ||||||
|     if reset_milestone(milestone): |     if reset_milestone(milestone): | ||||||
|             click.echo(f"Hard reset milestone {milestone}.") |         click.echo(f"Hard reset milestone {milestone}.") | ||||||
|         else: | 
 | ||||||
|             click.echo(f"Hard resetting milestone {milestone} failed.") |  | ||||||
| 
 | 
 | ||||||
| def init_app(app): | def init_app(app): | ||||||
|     app.teardown_appcontext(close_db) |     app.teardown_appcontext(close_db) | ||||||
|  | |||||||
| @ -3,6 +3,6 @@ REDEEMS={ | |||||||
|     "lurk": {"price": 1, "type": "counter", "info": "Let us know you're going to lurk."}, |     "lurk": {"price": 1, "type": "counter", "info": "Let us know you're going to lurk."}, | ||||||
|     "react": {"price": 200, "type": "note", "info": "Attach link to a video for me to react to."}, |     "react": {"price": 200, "type": "note", "info": "Attach link to a video for me to react to."}, | ||||||
|     "request": {"price": 100, "type": "note", "info": "Request a level, gamemode, skin, etc."}, |     "request": {"price": 100, "type": "note", "info": "Request a level, gamemode, skin, etc."}, | ||||||
|     "go_nap": {"type": "milestone", "info": "Streamer will go nap when the goal is reached.", "goal": 1000}, |     "go_nap": {"goal": 1000, "type": "milestone", "info": "Streamer will go nap when the goal is reached."}, | ||||||
|     "inactive": {"price": 100, "type": "note", "info": "Example redeem that is inactive by default", "category": ["inactive"]} |     "inactive": {"price": 100, "type": "note", "info": "Example redeem that is inactive by default", "category": ["inactive"]} | ||||||
| } | } | ||||||
| @ -22,10 +22,29 @@ def handle_redeem(message, user_id): | |||||||
|         return |         return | ||||||
| 
 | 
 | ||||||
|     db = get_db() |     db = get_db() | ||||||
|     price = current_app.config['REDEEMS'][redeem]["price"] |  | ||||||
|     redeem_type = current_app.config['REDEEMS'][redeem]["type"] |     redeem_type = current_app.config['REDEEMS'][redeem]["type"] | ||||||
|     points = read_users_points(db, user_id) |     points = read_users_points(db, user_id) | ||||||
| 
 | 
 | ||||||
|  |     # handle milestone first because it doesn't have a price | ||||||
|  |     if redeem_type == "milestone": | ||||||
|  |         if milestone_complete(db, redeem): | ||||||
|  |             send_chat(f"Can't redeem {redeem}, that milestone was already completed!") | ||||||
|  |         elif not note: | ||||||
|  |             send_chat(f"Cannot redeem {redeem}, no amount of points specified.") | ||||||
|  |         elif not note.isdigit(): | ||||||
|  |             send_chat(f"Cannot redeem {redeem}, amount of points is not an integer.") | ||||||
|  |         elif int(note) > points: | ||||||
|  |             send_chat(f"Can't redeem {redeem}, you're donating more points than you have.") | ||||||
|  |         elif add_to_milestone(db, user_id, redeem, int(note)): | ||||||
|  |             send_chat(f"Succesfully donated to {redeem} milestone!") | ||||||
|  |             if check_apply_milestone_completion(db, redeem): | ||||||
|  |                 send_chat(f"Milestone goal {redeem} complete!") | ||||||
|  |         else: | ||||||
|  |             send_chat(f"Redeeming milestone {redeem} failed.") | ||||||
|  |         return | ||||||
|  |      | ||||||
|  |     # handle redeems with price argument | ||||||
|  |     price = current_app.config['REDEEMS'][redeem]["price"] | ||||||
|     if not points or points < price: |     if not points or points < price: | ||||||
|         send_chat(f"Can't redeem {redeem}, you don't have enough points.") |         send_chat(f"Can't redeem {redeem}, you don't have enough points.") | ||||||
|         return |         return | ||||||
| @ -50,20 +69,5 @@ def handle_redeem(message, user_id): | |||||||
|             send_chat(f"{redeem} redeemed for {price} points.") |             send_chat(f"{redeem} redeemed for {price} points.") | ||||||
|         else: |         else: | ||||||
|             send_chat(f"Redeeming {redeem} failed.") |             send_chat(f"Redeeming {redeem} failed.") | ||||||
|     elif redeem_type == "milestone": |  | ||||||
|         if milestone_complete(db, redeem): |  | ||||||
|             send_chat(f"Can't redeem {redeem}, that milestone was already completed!") |  | ||||||
|         elif not note: |  | ||||||
|             send_chat(f"Cannot redeem {redeem}, no amount of points specified.") |  | ||||||
|         elif not note.isdigit(): |  | ||||||
|             send_chat(f"Cannot redeem {redeem}, amount of points is not an integer.") |  | ||||||
|         elif int(note) > points: |  | ||||||
|             send_chat(f"Can't redeem {redeem}, you're donating more points than you have.") |  | ||||||
|         elif add_to_milestone(db, user_id, redeem, int(note)): |  | ||||||
|             send_chat(f"Succesfully donated to {redeem} milestone!") |  | ||||||
|             if check_apply_milestone_completion(db, redeem): |  | ||||||
|                 send_chat(f"Milestone goal {redeem} complete!") |  | ||||||
|         else: |  | ||||||
|             send_chat(f"Redeeming {redeem} failed.") |  | ||||||
|     else: |     else: | ||||||
|         send_chat(f"{redeem} not redeemed, type of redeem not found.") |         send_chat(f"{redeem} not redeemed, type of redeem not found.") | ||||||
|  | |||||||
| @ -139,7 +139,11 @@ | |||||||
|                 {% for redeem, redeem_info in redeems.items() %} |                 {% for redeem, redeem_info in redeems.items() %} | ||||||
|                 <tbody> |                 <tbody> | ||||||
|                     <td>{{ prefix }}{{ redeem }}</td> |                     <td>{{ prefix }}{{ redeem }}</td> | ||||||
|  |                     {% if redeem_info["type"] == "milestone" %} | ||||||
|  |                     <td></td> | ||||||
|  |                     {% else %} | ||||||
|                     <td>{{ redeem_info["price"] }}</td> |                     <td>{{ redeem_info["price"] }}</td> | ||||||
|  |                     {% endif %} | ||||||
|                     <td>{{ redeem_info["type"] }}</td> |                     <td>{{ redeem_info["type"] }}</td> | ||||||
|                     {% if redeem_info["info"] %} |                     {% if redeem_info["info"] %} | ||||||
|                     <td>{{ redeem_info["info"] }}</td> |                     <td>{{ redeem_info["info"] }}</td> | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user