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:
Brian S. Stephan 2024-05-16 22:01:12 -05:00
parent 15df477fa4
commit 14a1c5ceb6
Signed by: bss
GPG Key ID: 3DE06D3180895FCB
3 changed files with 204 additions and 0 deletions

View File

@ -110,6 +110,29 @@ class IdleRPG(Plugin):
explicit_target=character.game.channel.name)
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,
penalty_log_attr: str):
"""Penalize a character and log them out, for a provided reason.
@ -135,5 +158,24 @@ class IdleRPG(Plugin):
except Character.DoesNotExist:
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

View File

@ -33,6 +33,20 @@
"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",
"pk": 1,
@ -42,6 +56,15 @@
"channel": 1
}
},
{
"model": "idlerpg.game",
"pk": 2,
"fields": {
"name": "level test",
"active": true,
"channel": 2
}
},
{
"model": "idlerpg.character",
"pk": 1,
@ -64,5 +87,28 @@
"time_penalized_notice": 0,
"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
}
}
]

View File

@ -3,6 +3,7 @@
SPDX-FileCopyrightText: © 2024 Brian S. Stephan <bss@incorporeal.org>
SPDX-License-Identifier: AGPL-3.0-or-later
"""
import datetime
import logging
import re
import unittest.mock as mock
@ -255,3 +256,118 @@ class IrcPluginTest(TestCase):
)
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