sleep: add plugin
This commit is contained in:
parent
ff88bfbb05
commit
091d0ecd72
|
@ -50,6 +50,14 @@
|
|||
"commands": ["choose"],
|
||||
"separator": "or"
|
||||
},
|
||||
"sleep": {
|
||||
"sleep_time": 23,
|
||||
"wakeup_time": 7,
|
||||
"snore": "Zzzz",
|
||||
"sleep_message": "/me falls asleep",
|
||||
"wakeup_message": "/me wakes up",
|
||||
"snore_rate": 1.0
|
||||
},
|
||||
"wikipedia": {
|
||||
"commands": ["science", "define"],
|
||||
"lang": "en",
|
||||
|
|
|
@ -93,6 +93,11 @@ class Bot(irc.client.SimpleIRCClient, Logger):
|
|||
self.log_d(f"Private message from {nick} to {target}: {message}")
|
||||
self.run_plugin_callbacks(event)
|
||||
|
||||
def on_ping(self, connection, event):
|
||||
"""Handle a ping; can be used as a random event timer."""
|
||||
self.log_d(f"Received ping from {event.target}.")
|
||||
self.run_plugin_callbacks(event)
|
||||
|
||||
def run(self):
|
||||
"""Connect the bot to server, join channels and start responding."""
|
||||
self.log_i("Starting Edmond.")
|
||||
|
|
75
edmond/plugins/sleep.py
Normal file
75
edmond/plugins/sleep.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
from datetime import datetime, timedelta
|
||||
|
||||
from edmond.plugin import Plugin
|
||||
from edmond.utils import proc
|
||||
|
||||
|
||||
class SleepPlugin(Plugin):
|
||||
"""Handle sleep state of the bot, snore a little bit."""
|
||||
|
||||
REQUIRED_CONFIGS = [
|
||||
"sleep_time", "wakeup_time", "snore", "sleep_message", "wakeup_message",
|
||||
"snore_rate"
|
||||
]
|
||||
|
||||
def __init__(self, bot):
|
||||
super().__init__(bot)
|
||||
|
||||
def on_ping(self, event):
|
||||
awake = self.get_runtime_value("awake")
|
||||
self.bot.log_w(f"{self.bot.channels}")
|
||||
in_sleep_hours = self.is_sleep_time(datetime.now())
|
||||
if awake and in_sleep_hours:
|
||||
self.fall_asleep()
|
||||
elif not awake:
|
||||
if not in_sleep_hours:
|
||||
self.wake_up()
|
||||
else:
|
||||
# Maybe snore.
|
||||
if proc(self.config["snore_rate"]):
|
||||
for channel in self.bot.channels:
|
||||
self.bot.say(channel, self.config["snore"])
|
||||
|
||||
def fall_asleep(self):
|
||||
for channel in self.bot.channels:
|
||||
self.bot.say(channel, self.config["sleep_message"])
|
||||
self.set_runtime_value("awake", False)
|
||||
|
||||
def wake_up(self):
|
||||
self.set_runtime_value("awake", True)
|
||||
for channel in self.bot.channels:
|
||||
self.bot.say(channel, self.config["wakeup_message"])
|
||||
|
||||
def is_sleep_time(self, now):
|
||||
"""Return True if the bot should be sleeping by now.
|
||||
|
||||
The sleep range can span over 2 days (e.g. 23 to 7) or be contained in a
|
||||
day (e.g. 0 to 8).
|
||||
"""
|
||||
sleep_time = self.config["sleep_time"]
|
||||
wakeup_time = self.config["wakeup_time"]
|
||||
current_hour = now.hour
|
||||
# If we're before the sleep hour, check that we are not in the
|
||||
# previous sleep range. Else just check today's range.
|
||||
if current_hour < sleep_time:
|
||||
ref_dt = now - timedelta(days=1)
|
||||
else:
|
||||
ref_dt = now
|
||||
sleep_dt, wakeup_dt = self._get_sleep_range(
|
||||
ref_dt,
|
||||
sleep_time,
|
||||
wakeup_time
|
||||
)
|
||||
return sleep_dt <= now < wakeup_dt
|
||||
|
||||
@staticmethod
|
||||
def _get_sleep_range(now, sleep_time, wakeup_time):
|
||||
"""Return the (start, end) datetime tuple using config values.
|
||||
|
||||
Properly accounts for a sleep range going around midnight.
|
||||
"""
|
||||
sleep_dt = now.replace(hour=sleep_time, minute=0, second=0)
|
||||
wakeup_dt = now.replace(hour=wakeup_time, minute=0, second=0)
|
||||
if wakeup_time < sleep_time:
|
||||
wakeup_dt += timedelta(days=1)
|
||||
return sleep_dt, wakeup_dt
|
0
edmond/plugins/tests/__init__.py
Normal file
0
edmond/plugins/tests/__init__.py
Normal file
47
edmond/plugins/tests/test_sleep.py
Normal file
47
edmond/plugins/tests/test_sleep.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
import unittest
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from ..sleep import SleepPlugin
|
||||
from edmond.tests.test_plugin import get_plugin_patcher
|
||||
|
||||
|
||||
class TestSleepPlugin(unittest.TestCase):
|
||||
|
||||
def test_is_sleep_time(self):
|
||||
with get_plugin_patcher(SleepPlugin):
|
||||
plugin = SleepPlugin()
|
||||
today = datetime.today()
|
||||
tomorrow = datetime.today() + timedelta(days=1)
|
||||
|
||||
# Given sleep time from 23:00 to 7:00
|
||||
plugin.config = {"sleep_time": 23, "wakeup_time": 7}
|
||||
# You should sleep within the sleep range.
|
||||
today_23_00 = today.replace(hour=23, minute=0, second=0)
|
||||
self.assertTrue(plugin.is_sleep_time(today_23_00))
|
||||
today_23_30 = today.replace(hour=23, minute=30, second=0)
|
||||
self.assertTrue(plugin.is_sleep_time(today_23_30))
|
||||
tomorrow_00_00 = tomorrow.replace(hour=0, minute=0, second=0)
|
||||
self.assertTrue(plugin.is_sleep_time(tomorrow_00_00))
|
||||
tomorrow_06_59 = tomorrow.replace(hour=6, minute=59, second=0)
|
||||
self.assertTrue(plugin.is_sleep_time(tomorrow_06_59))
|
||||
# But not outside the range.
|
||||
today_12_00 = today.replace(hour=12, minute=0, second=0)
|
||||
self.assertFalse(plugin.is_sleep_time(today_12_00))
|
||||
today_22_59 = today.replace(hour=22, minute=59, second=0)
|
||||
self.assertFalse(plugin.is_sleep_time(today_22_59))
|
||||
|
||||
# Given sleep time from 00:00 to 7:00
|
||||
plugin.config = {"sleep_time": 0, "wakeup_time": 7}
|
||||
# You should sleep within the sleep range.
|
||||
today_00_00 = today.replace(hour=0, minute=0, second=0)
|
||||
self.assertTrue(plugin.is_sleep_time(today_00_00))
|
||||
today_06_59 = today.replace(hour=6, minute=59, second=0)
|
||||
self.assertTrue(plugin.is_sleep_time(today_06_59))
|
||||
tomorrow_06_59 = tomorrow.replace(hour=6, minute=59, second=0)
|
||||
self.assertTrue(plugin.is_sleep_time(tomorrow_06_59))
|
||||
# But not outside the range.
|
||||
today_12_00 = today.replace(hour=12, minute=0, second=0)
|
||||
self.assertFalse(plugin.is_sleep_time(today_12_00))
|
||||
today_22_59 = today.replace(hour=22, minute=59, second=0)
|
||||
self.assertFalse(plugin.is_sleep_time(today_22_59))
|
0
edmond/tests/__init__.py
Normal file
0
edmond/tests/__init__.py
Normal file
6
edmond/tests/test_plugin.py
Normal file
6
edmond/tests/test_plugin.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from unittest.mock import patch
|
||||
|
||||
|
||||
def get_plugin_patcher(plugin_class):
|
||||
"""Return a context manager to patch a Plugin init."""
|
||||
return patch.object(plugin_class, "__init__", lambda bot: None)
|
Loading…
Reference in a new issue