get more IRC bot functionality under test
remove a character, get character status, and the main level up check thread Signed-off-by: Brian S. Stephan <bss@incorporeal.org>
This commit is contained in:
parent
15df477fa4
commit
14a1c5ceb6
@ -110,6 +110,29 @@ class IdleRPG(Plugin):
|
|||||||
explicit_target=character.game.channel.name)
|
explicit_target=character.game.channel.name)
|
||||||
return self.bot.reply(event, f"{character} has been successfully logged in.")
|
return self.bot.reply(event, f"{character} has been successfully logged in.")
|
||||||
|
|
||||||
|
def handle_remove(self, connection, event, match):
|
||||||
|
"""Handle the disabling of a character."""
|
||||||
|
hostmask = event.source
|
||||||
|
try:
|
||||||
|
character = Character.objects.get(enabled=True, hostmask=hostmask)
|
||||||
|
character.enabled = False
|
||||||
|
character.save()
|
||||||
|
return self.bot.reply(event, f"Character {character.name} has been disabled.")
|
||||||
|
except Character.DoesNotExist:
|
||||||
|
return self.bot.reply(event, "No character associated to your hostmask found (try logging in first).")
|
||||||
|
|
||||||
|
def handle_status(self, connection, event, match):
|
||||||
|
"""Handle the request for character/player status."""
|
||||||
|
hostmask = event.source
|
||||||
|
try:
|
||||||
|
character = Character.objects.get(enabled=True, hostmask=hostmask)
|
||||||
|
self.bot.reply(event, f"{character}, is {hostmask}.")
|
||||||
|
self.bot.reply(event, f"{character.name} is {Character.CHARACTER_STATUSES[character.status]}.")
|
||||||
|
if character.status == Character.CHARACTER_STATUS_LOGGED_IN:
|
||||||
|
self.bot.reply(event, f"{character.name} will level up on {character.next_level_str()}.")
|
||||||
|
except Character.DoesNotExist:
|
||||||
|
self.bot.reply(event, "No character associated to your hostmask found (try logging in first).")
|
||||||
|
|
||||||
def _handle_generic_penalty_and_logout(self, hostmask: str, channel: str, penalty: int, reason: str,
|
def _handle_generic_penalty_and_logout(self, hostmask: str, channel: str, penalty: int, reason: str,
|
||||||
penalty_log_attr: str):
|
penalty_log_attr: str):
|
||||||
"""Penalize a character and log them out, for a provided reason.
|
"""Penalize a character and log them out, for a provided reason.
|
||||||
@ -135,5 +158,24 @@ class IdleRPG(Plugin):
|
|||||||
except Character.DoesNotExist:
|
except Character.DoesNotExist:
|
||||||
logger.debug("no character found for %s", hostmask)
|
logger.debug("no character found for %s", hostmask)
|
||||||
|
|
||||||
|
def level_thread(self):
|
||||||
|
"""Check for characters who have leveled up, and log as such in the channel."""
|
||||||
|
while self.check_for_level_ups:
|
||||||
|
time.sleep(self.SLEEP_BETWEEN_LEVEL_CHECKS)
|
||||||
|
self._level_up_games()
|
||||||
|
|
||||||
|
def _level_up_games(self):
|
||||||
|
"""Find games under management and level up characters within them, updating their channel."""
|
||||||
|
logger.debug("checking for level ups in games")
|
||||||
|
for game in Game.objects.filter(active=True):
|
||||||
|
logger.debug("checking for level ups in %s", game)
|
||||||
|
for character in Character.objects.levelable(game):
|
||||||
|
logger.debug("going to try to level up %s", character)
|
||||||
|
character.level_up()
|
||||||
|
self.bot.reply(None, f"{character.name}, the {character.character_class}, has attained level "
|
||||||
|
f"{character.level}! Next level at {character.next_level_str()}.",
|
||||||
|
explicit_target=game.channel.name)
|
||||||
|
character.save()
|
||||||
|
|
||||||
|
|
||||||
plugin = IdleRPG
|
plugin = IdleRPG
|
||||||
|
46
tests/fixtures/simple_character.json
vendored
46
tests/fixtures/simple_character.json
vendored
@ -33,6 +33,20 @@
|
|||||||
"discord_bridge": ""
|
"discord_bridge": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"model": "ircbot.ircchannel",
|
||||||
|
"pk": 2,
|
||||||
|
"fields": {
|
||||||
|
"name": "#level_test",
|
||||||
|
"server": 1,
|
||||||
|
"autojoin": false,
|
||||||
|
"topic_msg": "",
|
||||||
|
"topic_time": "2024-05-06T05:10:25.154Z",
|
||||||
|
"topic_by": "",
|
||||||
|
"markov_learn_from_channel": true,
|
||||||
|
"discord_bridge": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"model": "idlerpg.game",
|
"model": "idlerpg.game",
|
||||||
"pk": 1,
|
"pk": 1,
|
||||||
@ -42,6 +56,15 @@
|
|||||||
"channel": 1
|
"channel": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"model": "idlerpg.game",
|
||||||
|
"pk": 2,
|
||||||
|
"fields": {
|
||||||
|
"name": "level test",
|
||||||
|
"active": true,
|
||||||
|
"channel": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"model": "idlerpg.character",
|
"model": "idlerpg.character",
|
||||||
"pk": 1,
|
"pk": 1,
|
||||||
@ -64,5 +87,28 @@
|
|||||||
"time_penalized_notice": 0,
|
"time_penalized_notice": 0,
|
||||||
"game": 1
|
"game": 1
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "idlerpg.character",
|
||||||
|
"pk": 2,
|
||||||
|
"fields": {
|
||||||
|
"name": "bss2",
|
||||||
|
"password": "pbkdf2_sha256$720000$A941t4dL96zzqeldCFucrr$Pof137/IjT3p//ZR+iYNoBnGmYPG6jLbNqenwMA3hHY=",
|
||||||
|
"hostmask": "bss2!bss@bss",
|
||||||
|
"status": "OFFLINE",
|
||||||
|
"character_class": "tester",
|
||||||
|
"level": 0,
|
||||||
|
"next_level": "2024-05-05T05:20:45.437Z",
|
||||||
|
"created": "2024-05-05T05:10:45.438Z",
|
||||||
|
"last_login": "2024-05-05T05:10:45.437Z",
|
||||||
|
"time_penalized_nick_change": 0,
|
||||||
|
"time_penalized_part": 0,
|
||||||
|
"time_penalized_quit": 0,
|
||||||
|
"time_penalized_logout": 0,
|
||||||
|
"time_penalized_kicked": 0,
|
||||||
|
"time_penalized_privmsg": 0,
|
||||||
|
"time_penalized_notice": 0,
|
||||||
|
"game": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
SPDX-FileCopyrightText: © 2024 Brian S. Stephan <bss@incorporeal.org>
|
SPDX-FileCopyrightText: © 2024 Brian S. Stephan <bss@incorporeal.org>
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
"""
|
"""
|
||||||
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import unittest.mock as mock
|
import unittest.mock as mock
|
||||||
@ -255,3 +256,118 @@ class IrcPluginTest(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.plugin.seen_hostmasks.remove('bss!bss@test_login')
|
self.plugin.seen_hostmasks.remove('bss!bss@test_login')
|
||||||
|
|
||||||
|
def test_remove(self):
|
||||||
|
"""Test the remove command."""
|
||||||
|
mock_event = mock.MagicMock()
|
||||||
|
mock_event.source = 'bss!bss@bss_remove'
|
||||||
|
mock_event.recursing = False
|
||||||
|
|
||||||
|
game = Game.objects.get(pk=2)
|
||||||
|
test_char = Character.objects.register('test_remove', game, 'test', 'bss!bss@bss_remove', 'tester')
|
||||||
|
match = re.match(IdleRPG.LOGIN_COMMAND_PATTERN, 'REMOVEME')
|
||||||
|
self.plugin.handle_remove(self.mock_connection, mock_event, match)
|
||||||
|
|
||||||
|
assert test_char.enabled is True
|
||||||
|
refetch = Character.objects.get(name='test_remove')
|
||||||
|
assert refetch.enabled is False
|
||||||
|
self.mock_bot.reply.assert_has_calls([
|
||||||
|
mock.call(mock_event, "Character test_remove has been disabled."),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_remove_no_match(self):
|
||||||
|
"""Test the remove command doesn't do anything if the hostname isn't found."""
|
||||||
|
mock_event = mock.MagicMock()
|
||||||
|
mock_event.source = 'bss2!bss@bss_remove'
|
||||||
|
mock_event.recursing = False
|
||||||
|
|
||||||
|
game = Game.objects.get(pk=2)
|
||||||
|
test_char = Character.objects.register('test_remove', game, 'test', 'bss!bss@bss_remove', 'tester')
|
||||||
|
match = re.match(IdleRPG.LOGIN_COMMAND_PATTERN, 'REMOVEME')
|
||||||
|
self.plugin.handle_remove(self.mock_connection, mock_event, match)
|
||||||
|
|
||||||
|
assert test_char.enabled is True
|
||||||
|
refetch = Character.objects.get(name='test_remove')
|
||||||
|
assert refetch.enabled is True
|
||||||
|
self.mock_bot.reply.assert_has_calls([
|
||||||
|
mock.call(mock_event, "No character associated to your hostmask found (try logging in first)."),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_status(self):
|
||||||
|
"""Test the status command."""
|
||||||
|
mock_event = mock.MagicMock()
|
||||||
|
mock_event.source = 'bss!bss@bss'
|
||||||
|
mock_event.recursing = False
|
||||||
|
|
||||||
|
self.plugin.seen_hostmasks.add('bss!bss@bss')
|
||||||
|
match = re.match(IdleRPG.LOGIN_COMMAND_PATTERN, 'STATUS')
|
||||||
|
self.plugin.handle_status(self.mock_connection, mock_event, match)
|
||||||
|
|
||||||
|
self.mock_bot.reply.assert_has_calls([
|
||||||
|
mock.call(mock_event, "bss, the level 0 tester, is bss!bss@bss."),
|
||||||
|
mock.call(mock_event, "bss is logged in."),
|
||||||
|
mock.call(mock_event, "bss will level up on 2024-05-05 05:20:45 UTC."),
|
||||||
|
])
|
||||||
|
assert self.mock_bot.reply.call_count == 3
|
||||||
|
|
||||||
|
self.plugin.seen_hostmasks.remove('bss!bss@bss')
|
||||||
|
|
||||||
|
def test_status_logged_out(self):
|
||||||
|
"""Test the status command."""
|
||||||
|
mock_event = mock.MagicMock()
|
||||||
|
mock_event.source = 'bss2!bss@bss'
|
||||||
|
mock_event.recursing = False
|
||||||
|
|
||||||
|
self.plugin.seen_hostmasks.add('bss2!bss@bss')
|
||||||
|
match = re.match(IdleRPG.LOGIN_COMMAND_PATTERN, 'STATUS')
|
||||||
|
self.plugin.handle_status(self.mock_connection, mock_event, match)
|
||||||
|
|
||||||
|
self.mock_bot.reply.assert_has_calls([
|
||||||
|
mock.call(mock_event, "bss2, the level 0 tester, is bss2!bss@bss."),
|
||||||
|
mock.call(mock_event, "bss2 is offline."),
|
||||||
|
])
|
||||||
|
assert self.mock_bot.reply.call_count == 2
|
||||||
|
|
||||||
|
self.plugin.seen_hostmasks.remove('bss2!bss@bss')
|
||||||
|
|
||||||
|
def test_status_no_match(self):
|
||||||
|
"""Test the status command."""
|
||||||
|
mock_event = mock.MagicMock()
|
||||||
|
mock_event.source = 'bss3!bss@bss'
|
||||||
|
mock_event.recursing = False
|
||||||
|
|
||||||
|
self.plugin.seen_hostmasks.add('bss3!bss@bss')
|
||||||
|
match = re.match(IdleRPG.LOGIN_COMMAND_PATTERN, 'STATUS')
|
||||||
|
self.plugin.handle_status(self.mock_connection, mock_event, match)
|
||||||
|
|
||||||
|
self.mock_bot.reply.assert_has_calls([
|
||||||
|
mock.call(mock_event, "No character associated to your hostmask found (try logging in first)."),
|
||||||
|
])
|
||||||
|
assert self.mock_bot.reply.call_count == 1
|
||||||
|
|
||||||
|
self.plugin.seen_hostmasks.remove('bss3!bss@bss')
|
||||||
|
|
||||||
|
def test_level_up_games(self):
|
||||||
|
"""Test the walking of games and characters to level them up."""
|
||||||
|
game = Game.objects.get(pk=2)
|
||||||
|
char_1 = Character.objects.register('test_level_1', game, 'test', 'test_1!test@test', 'tester')
|
||||||
|
char_2 = Character.objects.register('test_level_2', game, 'test', 'test_2!test@test', 'tester')
|
||||||
|
char_2.log_out()
|
||||||
|
level_time = datetime.datetime(year=2024, month=5, day=16, hour=00, minute=0, second=0)
|
||||||
|
char_1.next_level = level_time
|
||||||
|
char_2.next_level = level_time
|
||||||
|
char_1.save()
|
||||||
|
char_2.save()
|
||||||
|
# only one should level up, since char_2 isn't logged in
|
||||||
|
self.plugin._level_up_games()
|
||||||
|
|
||||||
|
char_1 = Character.objects.get(pk=char_1.pk)
|
||||||
|
char_2 = Character.objects.get(pk=char_2.pk)
|
||||||
|
assert char_1.level == 1
|
||||||
|
assert char_2.level == 0
|
||||||
|
assert char_1.next_level != char_2.next_level
|
||||||
|
self.mock_bot.reply.assert_has_calls([
|
||||||
|
mock.call(None, "test_level_1, the tester, has attained level 1! Next level at 2024-05-16 00:10:00 UTC.",
|
||||||
|
explicit_target=game.channel.name),
|
||||||
|
])
|
||||||
|
assert self.mock_bot.reply.call_count == 2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user