notes: add plugin, only taking notes
This commit is contained in:
parent
9ab1812938
commit
70cdcad604
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
config.json
|
||||
storage.json
|
||||
resources/*
|
||||
!resources/.gitkeep
|
||||
|
|
|
@ -43,3 +43,4 @@ Missing features
|
|||
- [ ] Youtube: parsing for title, requests for channel or video
|
||||
- [ ] Command aliases
|
||||
- [ ] Question aliases
|
||||
- [ ] Various macros
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"alternative_nicks": ["edmon", "edmond"],
|
||||
"channels": ["#idi0crates"],
|
||||
"speak_delay": 0.5,
|
||||
"storage_file": "storage.json",
|
||||
"resources_dir": "resources",
|
||||
"error_message": "An error occured, sorry!",
|
||||
"plugins": {
|
||||
|
@ -37,6 +38,10 @@
|
|||
"pissed": "Pissed off..."
|
||||
}
|
||||
},
|
||||
"notes": {
|
||||
"commands": ["note down"],
|
||||
"content_regex": "for (P:<target>\\S+) (P:<note>.+)"
|
||||
},
|
||||
"random": {
|
||||
"commands": ["choose"],
|
||||
"separator": "or"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import importlib
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
@ -17,6 +18,7 @@ class Bot(irc.client.SimpleIRCClient, Logger):
|
|||
self.logger = logger
|
||||
self.plugins = []
|
||||
self.values = {}
|
||||
self.storage = self._get_storage()
|
||||
|
||||
@property
|
||||
def nick(self):
|
||||
|
@ -26,6 +28,28 @@ class Bot(irc.client.SimpleIRCClient, Logger):
|
|||
def names(self):
|
||||
return (self.config["nick"], *self.config["alternative_nicks"])
|
||||
|
||||
def _get_storage(self):
|
||||
"""Load data from storage."""
|
||||
try:
|
||||
with open(self.config["storage_file"], "rt") as storage_file:
|
||||
storage = json.load(storage_file)
|
||||
self.log_d("Loaded storage file.")
|
||||
return storage
|
||||
except (OSError, json.decoder.JSONDecodeError) as exc:
|
||||
self.log_e(f"Could not load storage file: {exc}")
|
||||
self.log_w("If it's not the first time Edm0nd is run, you may lose"
|
||||
" data when closing the program.")
|
||||
return {}
|
||||
|
||||
def _save_storage(self):
|
||||
"""Save storage data to disk."""
|
||||
try:
|
||||
with open(self.config["storage_file"], "wt") as storage_file:
|
||||
json.dump(self.storage, storage_file, indent=2, sort_keys=True)
|
||||
self.log_d("Saved storage file.")
|
||||
except (OSError, json.decoder.JSONEncodeError) as exc:
|
||||
self.log_e(f"Could not save storage file: {exc}")
|
||||
|
||||
def on_welcome(self, connection, event):
|
||||
self.log_i(f"Connected to server {event.source}.")
|
||||
self.run_plugin_callbacks(event)
|
||||
|
@ -63,6 +87,7 @@ class Bot(irc.client.SimpleIRCClient, Logger):
|
|||
self.start()
|
||||
except KeyboardInterrupt:
|
||||
self.log_i("Stopping Edmond.")
|
||||
self._save_storage()
|
||||
|
||||
def load_plugins(self):
|
||||
"""Load all installed plugins."""
|
||||
|
@ -97,6 +122,7 @@ class Bot(irc.client.SimpleIRCClient, Logger):
|
|||
self.connection.privmsg(target, message)
|
||||
|
||||
def run_plugin_callbacks(self, event):
|
||||
"""Run appropriate callbacks for each plugin."""
|
||||
etype = event.type
|
||||
for plugin in filter(lambda p: p.is_ready, self.plugins):
|
||||
callbacks = plugin.callbacks
|
||||
|
|
|
@ -47,7 +47,11 @@ class Plugin:
|
|||
return not missing
|
||||
|
||||
def get_runtime_value(self, key, ns=None):
|
||||
"""Get a value from the plugin runtime dict."""
|
||||
"""Get a value from the plugin runtime dict.
|
||||
|
||||
This will get the value from the plugin namespace, but it is possible to
|
||||
get runtime values from other plugins using their name as `ns`.
|
||||
"""
|
||||
if ns is None:
|
||||
ns = self.name
|
||||
return self.bot.values[ns].get(key)
|
||||
|
@ -56,6 +60,26 @@ class Plugin:
|
|||
"""Set a value in the plugin runtime dict."""
|
||||
self.bot.values[self.name][key] = value
|
||||
|
||||
def get_storage_value(self, key):
|
||||
"""Get a value from the plugin persistent storage."""
|
||||
return self.bot.storage.get(self.name, {}).get(key)
|
||||
|
||||
def set_storage_value(self, key, value):
|
||||
"""Set a value in the plugin persistent storage."""
|
||||
if self.name not in self.bot.storage:
|
||||
self.bot.storage[self.name] = {key: value}
|
||||
else:
|
||||
self.bot.storage[self.name][key] = value
|
||||
|
||||
def append_storage_list_value(self, key, value):
|
||||
"""Append a value to a list in the plugin persistent storage."""
|
||||
if self.name not in self.bot.storage:
|
||||
self.bot.storage[self.name] = {key: [value]}
|
||||
elif key not in self.bot.storage[self.name]:
|
||||
self.bot.storage[self.name][key] = [value]
|
||||
else:
|
||||
self.bot.storage[self.name][key].append(value)
|
||||
|
||||
def should_answer_question(self, message):
|
||||
"""Store Question in object and return True if it should answer it."""
|
||||
words = message.split()
|
||||
|
@ -74,7 +98,10 @@ class Plugin:
|
|||
"""Store Command in object and return True if it should handle it."""
|
||||
command = self.parse_command(message, no_content=no_content)
|
||||
commands = self.config.get("commands", [])
|
||||
if command and any(c == command.ident for c in commands):
|
||||
if (
|
||||
any(command.ident == c for c in commands) or
|
||||
(no_content and any(command.ident.startswith(c) for c in commands))
|
||||
):
|
||||
self.command = command
|
||||
self.bot.log_d(f"Processing command from plugin {self.name}.")
|
||||
return True
|
||||
|
|
42
edmond/plugins/notes.py
Normal file
42
edmond/plugins/notes.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
import re
|
||||
|
||||
from edmond.plugin import Plugin
|
||||
|
||||
|
||||
class NotesPlugin(Plugin):
|
||||
|
||||
REQUIRED_CONFIGS = ["commands", "content_regex"]
|
||||
|
||||
def __init__(self, bot):
|
||||
super().__init__(bot)
|
||||
self._content_re = None
|
||||
|
||||
@property
|
||||
def content_re(self):
|
||||
if self._content_re is None:
|
||||
self._content_re = re.compile(self.config["content_regex"])
|
||||
return self._content_re
|
||||
|
||||
def on_pubmsg(self, event):
|
||||
if not self.should_handle_command(event.arguments[0], no_content=True):
|
||||
return False
|
||||
|
||||
# "note down" command.
|
||||
command0 = self.config["commands"][0]
|
||||
if self.command.ident.startswith(command0):
|
||||
content = self.command.ident[len(command0):].strip()
|
||||
match = self.content_re.match(content)
|
||||
if not match:
|
||||
return False
|
||||
groups = match.groupdict()
|
||||
if any(k not in groups for k in ("target", "note")):
|
||||
return False
|
||||
target = groups["target"]
|
||||
message = groups["note"]
|
||||
self.bot.log_d(f"Noting for {target}: {message}")
|
||||
note = {
|
||||
"sender": event.source.nick,
|
||||
"dest": target,
|
||||
"message": message
|
||||
}
|
||||
self.append_storage_list_value("notes", note)
|
|
@ -13,14 +13,10 @@ class RandomPlugin(Plugin):
|
|||
def on_pubmsg(self, event):
|
||||
if not self.should_handle_command(event.arguments[0]):
|
||||
return False
|
||||
if self.command.ident == "choose":
|
||||
self.pick_random(event.target)
|
||||
|
||||
def pick_random(self, target):
|
||||
separator = self.config["separator"]
|
||||
choices = self.command.content.split(f" {separator} ")
|
||||
self.bot.log_d(f"Choices: {choices}")
|
||||
if len(choices):
|
||||
choice = random.choice(choices)
|
||||
if choice:
|
||||
self.bot.say(target, choice)
|
||||
self.bot.say(event.target, choice)
|
||||
|
|
Loading…
Reference in a new issue