Game Adapter Class: Allow superusers to moderate bots.
This commit is contained in:
parent
2c8b369d14
commit
b9905e5fc8
|
@ -0,0 +1,2 @@
|
||||||
|
[connect_four]
|
||||||
|
superusers = ["user@example.com"]
|
|
@ -45,8 +45,8 @@ class ConnectFourBotHandler(GameAdapter):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
game_name = 'Connect Four'
|
game_name = 'Connect Four'
|
||||||
bot_name = 'connect_four'
|
bot_name = 'connect_four'
|
||||||
move_help_message = '* To make your move during a game, type\n' + \
|
move_help_message = '* To make your move during a game, type\n' \
|
||||||
'```move <column-number>```'
|
'```move <column-number>```'
|
||||||
move_regex = 'move (\d)$'
|
move_regex = 'move (\d)$'
|
||||||
model = ConnectFourModel
|
model = ConnectFourModel
|
||||||
gameMessageHandler = ConnectFourMessageHandler
|
gameMessageHandler = ConnectFourMessageHandler
|
||||||
|
|
|
@ -25,8 +25,12 @@ can only run a single game at a time*
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
The Connect Four Bot does not require a config file or API key.
|
To set moderators for the bot, modify the connect_four.conf
|
||||||
It can be used without setup.
|
file as shown:
|
||||||
|
|
||||||
|
superusers = ["user@example.com", "user@example2.com", ...]
|
||||||
|
|
||||||
|
Moderators can run ```force reset``` in case any user abuse the bot
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -61,7 +65,7 @@ another user.
|
||||||
player to decline an invitation to play Connect Four against
|
player to decline an invitation to play Connect Four against
|
||||||
another user.
|
another user.
|
||||||
|
|
||||||
7. ```withdraw invitation``` : a command that can only be run by the
|
7. ```cancel game``` : a command that can only be run by the
|
||||||
inviter to withdraw their invitation to play. Especially
|
inviter to withdraw their invitation to play. Especially
|
||||||
useful if a player does not respond to an invitation for a
|
useful if a player does not respond to an invitation for a
|
||||||
long period of time.
|
long period of time.
|
||||||
|
@ -75,3 +79,7 @@ the user to confirm they wish to forfeit the game.
|
||||||
|
|
||||||
10. ```confirm quit``` : causes the user that runs this command
|
10. ```confirm quit``` : causes the user that runs this command
|
||||||
to forfeit the game.
|
to forfeit the game.
|
||||||
|
|
||||||
|
11. ```force reset``` : a command that can only be run by the bot
|
||||||
|
owner and moderators (see 'Usage' for specifying). Destroys any
|
||||||
|
game currently being run if users are abusing the bot
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
# @TODO: place bot owner name in config file, allow bot owner to run special commands
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import json
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
# @TODO: allow superusers
|
|
||||||
class InputVerification(object):
|
class InputVerification(object):
|
||||||
def __init__(self, move_regex):
|
def __init__(self, move_regex, superusers):
|
||||||
self.move_regex = move_regex
|
self.move_regex = move_regex
|
||||||
self.verified_commands = {
|
self.verified_commands = {
|
||||||
'waiting': ['start game with computer', 'start game with \w+@\w+\.\w+'],
|
'waiting': ['start game with computer', 'start game with \w+@\w+\.\w+'],
|
||||||
|
@ -13,17 +11,16 @@ class InputVerification(object):
|
||||||
'playing': [[move_regex, 'quit', 'confirm quit'], ['quit', 'confirm quit']]
|
'playing': [[move_regex, 'quit', 'confirm quit'], ['quit', 'confirm quit']]
|
||||||
}
|
}
|
||||||
self.all_valid_commands = ['help', 'status', 'start game with computer', 'start game with \w+@\w+\.\w+',
|
self.all_valid_commands = ['help', 'status', 'start game with computer', 'start game with \w+@\w+\.\w+',
|
||||||
'withdraw invitation', 'accept', 'decline', self.move_regex, 'quit', 'confirm quit']
|
'withdraw invitation', 'accept', 'decline', self.move_regex, 'quit', 'confirm quit', 'force reset']
|
||||||
|
self.superusers = superusers
|
||||||
|
|
||||||
verified_users = []
|
verified_users = []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def permission_lacking_message(self, command):
|
def permission_lacking_message(self, command):
|
||||||
return 'Sorry, but you can\'t run the command ```' + command + '```'
|
return 'Sorry, but you can\'t run the command ```' + command + '```'
|
||||||
|
|
||||||
def update_commands(self, turn):
|
def update_commands(self, turn):
|
||||||
self.verified_commands['playing'][-1 * turn + 1].remove(self.move_regex)
|
self.verified_commands['playing'] = [['quit', 'confirm quit'], ['quit', 'confirm quit']]
|
||||||
self.verified_commands['playing'][turn].append(self.move_regex)
|
self.verified_commands['playing'][turn].append(self.move_regex)
|
||||||
|
|
||||||
def reset_commands(self):
|
def reset_commands(self):
|
||||||
|
@ -50,6 +47,9 @@ class InputVerification(object):
|
||||||
|
|
||||||
return self.regex_match_in_array(command_array, command)
|
return self.regex_match_in_array(command_array, command)
|
||||||
|
|
||||||
|
def verify_superuser(self, user):
|
||||||
|
return user in self.superusers
|
||||||
|
|
||||||
class StateManager(object):
|
class StateManager(object):
|
||||||
def __init__(self, main_bot_handler):
|
def __init__(self, main_bot_handler):
|
||||||
self.users = None
|
self.users = None
|
||||||
|
@ -331,11 +331,9 @@ class GameAdapter(object):
|
||||||
self.move_help_message = move_help_message
|
self.move_help_message = move_help_message
|
||||||
self.model = model
|
self.model = model
|
||||||
self.gameMessageHandler = gameMessageHandler()
|
self.gameMessageHandler = gameMessageHandler()
|
||||||
self.inputVerification = InputVerification(move_regex)
|
self.inputVerification = InputVerification(move_regex, [])
|
||||||
|
|
||||||
def get_stored_data(self):
|
def get_stored_data(self):
|
||||||
# @TODO: remove this comment when you create super users
|
|
||||||
# return self.data # Uncomment and rerun bot to reset data if users are abusing the bot
|
|
||||||
return self.bot_handler.storage.get(self.bot_name)
|
return self.bot_handler.storage.get(self.bot_name)
|
||||||
|
|
||||||
def update_data(self):
|
def update_data(self):
|
||||||
|
@ -343,13 +341,17 @@ class GameAdapter(object):
|
||||||
|
|
||||||
if 'users' in self.data:
|
if 'users' in self.data:
|
||||||
self.inputVerification.verified_users = self.data['users']
|
self.inputVerification.verified_users = self.data['users']
|
||||||
|
else:
|
||||||
|
self.inputVerification.verified_users = []
|
||||||
|
|
||||||
if self.state == 'inviting':
|
if self.state == 'inviting':
|
||||||
self.invitationHandler = InvitationHandler(self)
|
self.invitationHandler = InvitationHandler(self)
|
||||||
self.gameHandler = GameHandler(self, self.data['game_type'], self.model())
|
self.gameHandler = GameHandler(self, self.data['game_type'], self.model())
|
||||||
|
|
||||||
elif self.state == 'playing':
|
elif self.state == 'playing':
|
||||||
self.gameHandler = GameHandler(self, self.data['game_type'], self.model(), board = self.data['board'], turn = self.data['turn'], )
|
self.gameHandler = GameHandler(self, self.data['game_type'], self.model(),
|
||||||
|
board = self.data['board'], turn = self.data['turn'])
|
||||||
|
self.inputVerification.update_commands(self.data['turn'])
|
||||||
|
|
||||||
def put_stored_data(self):
|
def put_stored_data(self):
|
||||||
self.data = {}
|
self.data = {}
|
||||||
|
@ -470,11 +472,26 @@ class GameAdapter(object):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def initialize(self, bot_handler):
|
def initialize(self, bot_handler):
|
||||||
|
self.config_info = bot_handler.get_config_info('connect_four')
|
||||||
|
if self.config_info:
|
||||||
|
self.inputVerification.superusers = json.loads(self.config_info['superusers'])
|
||||||
self.gameCreator = GameCreator(self)
|
self.gameCreator = GameCreator(self)
|
||||||
self.inputVerification.reset_commands()
|
self.inputVerification.reset_commands()
|
||||||
|
|
||||||
if not bot_handler.storage.contains(self.bot_name):
|
if not bot_handler.storage.contains(self.bot_name):
|
||||||
bot_handler.storage.put(self.bot_name, self.data)
|
bot_handler.storage.put(self.bot_name, self.data)
|
||||||
|
|
||||||
|
def force_reset(self, sender):
|
||||||
|
for user in self.inputVerification.verified_users:
|
||||||
|
self.send_message(user, 'A bot moderator determined you were abusing the bot, and quit your game.'
|
||||||
|
' Please make sure you finish all your games in a timely fashion.')
|
||||||
|
|
||||||
|
self.send_message(sender, 'The game has been force reset')
|
||||||
|
|
||||||
|
self.data = data = {'state': 'waiting'}
|
||||||
|
self.update_data()
|
||||||
|
self.put_stored_data()
|
||||||
|
|
||||||
def handle_message(self, message, bot_handler):
|
def handle_message(self, message, bot_handler):
|
||||||
self.bot_handler = bot_handler
|
self.bot_handler = bot_handler
|
||||||
|
|
||||||
|
@ -489,7 +506,10 @@ class GameAdapter(object):
|
||||||
'Type ```help``` to see a full list of commands.')
|
'Type ```help``` to see a full list of commands.')
|
||||||
return
|
return
|
||||||
|
|
||||||
# Messages that can be sent regardless of state or user
|
elif self.inputVerification.verify_superuser(sender) and content.lower() == 'force reset':
|
||||||
|
self.force_reset(sender)
|
||||||
|
return
|
||||||
|
|
||||||
elif content.lower() == 'help' or content == '':
|
elif content.lower() == 'help' or content == '':
|
||||||
self.send_message(sender, self.help_message())
|
self.send_message(sender, self.help_message())
|
||||||
return
|
return
|
||||||
|
|
|
@ -137,7 +137,7 @@ class TestConnectFourBot(BotTestCase):
|
||||||
|
|
||||||
def test_move(self):
|
def test_move(self):
|
||||||
self.verify_response('move 8', 'That\'s an invalid move. Please specify a column '
|
self.verify_response('move 8', 'That\'s an invalid move. Please specify a column '
|
||||||
'between 1 and 7 with at least one open spot.', 0, data=self.start_two_player_data)
|
'between 1 and 7 with at least one open spot.', 0, data=self.start_two_player_data)
|
||||||
self.verify_response('move 1', 'You placed your token in column 1.', 0, data=self.start_two_player_data)
|
self.verify_response('move 1', 'You placed your token in column 1.', 0, data=self.start_two_player_data)
|
||||||
self.verify_response('move 1', '**the Computer moved in column 1**.', 3, data=self.start_one_player_data, computer_move=0)
|
self.verify_response('move 1', '**the Computer moved in column 1**.', 3, data=self.start_one_player_data, computer_move=0)
|
||||||
|
|
||||||
|
@ -149,16 +149,19 @@ class TestConnectFourBot(BotTestCase):
|
||||||
def test_quit(self):
|
def test_quit(self):
|
||||||
self.verify_response('quit', 'Are you sure you want to quit? You will forfeit the game!\n'
|
self.verify_response('quit', 'Are you sure you want to quit? You will forfeit the game!\n'
|
||||||
'Type ```confirm quit``` to forfeit.', 0, data=self.start_two_player_data)
|
'Type ```confirm quit``` to forfeit.', 0, data=self.start_two_player_data)
|
||||||
|
|
||||||
def test_confirm_quit(self):
|
|
||||||
self.verify_response('confirm quit', '**You have forfeit the game**\nSorry, but you lost :cry:', 0, data=self.start_two_player_data)
|
self.verify_response('confirm quit', '**You have forfeit the game**\nSorry, but you lost :cry:', 0, data=self.start_two_player_data)
|
||||||
|
|
||||||
|
def test_force_reset(self):
|
||||||
|
with self.mock_config_info({'superusers': '["foo@example.com"]'}):
|
||||||
|
self.verify_response('force reset', 'The game has been force reset', 1, data=self.start_one_player_data)
|
||||||
|
|
||||||
def test_privilege_check(self):
|
def test_privilege_check(self):
|
||||||
self.verify_response('move 4', 'Sorry, but you can\'t run the command ```move 4```', 0, data=self.inviting_two_player_data)
|
self.verify_response('move 4', 'Sorry, but you can\'t run the command ```move 4```', 0, data=self.inviting_two_player_data)
|
||||||
self.verify_response('start game with computer', 'Sorry, but other users are already using the bot.'
|
self.verify_response('start game with computer', 'Sorry, but other users are already using the bot.'
|
||||||
'Type ```status``` to see the current status of the bot.', 0, data=self.inviting_two_player_data, user = 'foo3@example.com')
|
'Type ```status``` to see the current status of the bot.', 0, data=self.inviting_two_player_data, user = 'foo3@example.com')
|
||||||
self.verify_response('quit', 'Sorry, but you can\'t run the command ```quit```', 0)
|
self.verify_response('quit', 'Sorry, but you can\'t run the command ```quit```', 0)
|
||||||
self.verify_response('accept', 'Sorry, but you can\'t run the command ```accept```', 0, data=self.end_two_player_data)
|
self.verify_response('accept', 'Sorry, but you can\'t run the command ```accept```', 0, data=self.end_two_player_data)
|
||||||
|
self.verify_response('force reset', 'Sorry, but you can\'t run the command ```force reset```', 0)
|
||||||
|
|
||||||
def test_connect_four_logic(self):
|
def test_connect_four_logic(self):
|
||||||
def confirmAvailableMoves(good_moves, bad_moves, board):
|
def confirmAvailableMoves(good_moves, bad_moves, board):
|
||||||
|
|
Loading…
Reference in a new issue