move all commands to plugins

This commit is contained in:
John Burwell 2023-04-27 15:06:55 -05:00
parent d6e3f02ca1
commit acc6061c62
18 changed files with 679 additions and 292 deletions

View File

@ -16,5 +16,13 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
__all__ = ["rsbbs", "bbs", "message", "parser"] __all__ = ['config', 'console',
'controller', 'parser', 'pluginloader']
__version__ = "0.2.0" __version__ = "0.2.0"
# from .config import Config
# from .console import Console
# from .controller import Controller
# from .parser import Parser
# from .pluginloader import PluginLoader

View File

@ -1,111 +0,0 @@
#!/usr/bin/env python
#
# Really Simple BBS - a really simple BBS for ax.25 packet radio.
# Copyright (C) 2023 John Burwell <john@atatdotdot.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
class Commands():
def __init__(self, responder):
self.responder = responder
@property
def commands(self):
commands = [
# (name,
# aliases,
# helpmsg,
# function,
# arg:
# arg attributes...,
# )
('bye',
['b', 'q'],
'Sign off and disconnect',
self.responder.bye,
{}),
('delete',
['d', 'k'],
'Delete a message',
self.responder.delete,
{'number':
{'help':
'The numeric index of the message to delete'}},),
('deletem',
['dm', 'km'],
'Delete all your messages',
self.responder.delete_mine,
{}),
('help',
['h', '?'],
'Show help',
self.responder.help,
{}),
('heard',
['j'],
'Show heard stations log',
self.responder.heard,
{}),
('list',
['l'],
'List all messages',
self.responder.list,
{}),
('listm',
['lm'],
'List only messages addressed to you',
self.responder.list_mine,
{}),
('read',
['r'],
'Read messages',
self.responder.read,
{'number': {'help': 'Message number to read'}}),
('readm',
['rm'],
'Read only messages addressed to you',
self.responder.read_mine,
{}),
('send',
['s'],
'Send a new message to a user',
self.responder.send,
{
'--callsign': {'help': 'Message recipient callsign'},
'--subject': {'help': 'Message subject'},
'--message': {'help': 'Message'},
},),
('sendp',
['sp'],
'Send a private message to a user',
self.responder.send_private,
{
'--callsign': {'help': 'Message recipient callsign'},
'--subject': {'help': 'Message subject'},
'--message': {'help': 'Message'},
},),]
return commands

View File

@ -16,15 +16,16 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
import os
import sys import sys
import sqlalchemy.exc import sqlalchemy.exc
import rsbbs import rsbbs
from rsbbs.commands import Commands
from rsbbs.config import Config from rsbbs.config import Config
from rsbbs.controller import Controller from rsbbs.controller import Controller
from rsbbs.parser import Parser from rsbbs.parser import Parser
from rsbbs.pluginloader import PluginLoader
# Main UI console class # Main UI console class
@ -35,33 +36,42 @@ class Console():
self.config = config self.config = config
self.controller = controller self.controller = controller
self.commands = Commands(self) self.parser = Parser()
self.parser = Parser(self.commands)
self.pluginloader = PluginLoader(self)
self.pluginloader.load_plugins()
# #
# Input and output # Input and output
# #
def _read_line(self, prompt): def read_enter(self, prompt) -> None:
"""Wait for the user to press enter.
"""
if prompt:
self.write_output(prompt)
sys.stdin.readline().strip()
def read_line(self, prompt) -> str:
"""Read a single line of input, with an optional prompt, """Read a single line of input, with an optional prompt,
until we get something. until we get something.
""" """
output = None output = None
while not output: while not output:
if prompt: if prompt:
self._write_output(prompt) self.write_output(prompt)
input = sys.stdin.readline().strip() input = sys.stdin.readline().strip()
if input != "": if input != "":
output = input output = input
return output return output
def _read_multiline(self, prompt): def read_multiline(self, prompt) -> str:
"""Read multiple lines of input, with an optional prompt, """Read multiple lines of input, with an optional prompt,
until the user enters '/ex' by itself on a line. until the user enters '/ex' by itself on a line.
""" """
output = [] output = []
if prompt: if prompt:
self._write_output(prompt) self.write_output(prompt)
while True: while True:
line = sys.stdin.readline() line = sys.stdin.readline()
if line.lower().strip() == "/ex": if line.lower().strip() == "/ex":
@ -70,14 +80,14 @@ class Console():
output.append(line) output.append(line)
return ''.join(output) return ''.join(output)
def _write_output(self, output): def write_output(self, output) -> None:
"""Write something to stdout.""" """Write something to stdout."""
sys.stdout.write(output + '\r\n') sys.stdout.write(output + '\r\n')
def print_configuration(self): def print_configuration(self) -> None:
self._write_output(repr(self.config)) self.write_output(repr(self.config))
def print_greeting(self): def print_greeting(self) -> None:
# Show greeting # Show greeting
greeting = [] greeting = []
greeting.append(f"[RSBBS-{rsbbs.__version__}] listening on " greeting.append(f"[RSBBS-{rsbbs.__version__}] listening on "
@ -90,7 +100,7 @@ class Console():
greeting.append("For help, enter 'h'") greeting.append("For help, enter 'h'")
self._write_output('\r\n'.join(greeting)) self.write_output('\r\n'.join(greeting))
def print_message(self, message): def print_message(self, message):
"""Print an individual message.""" """Print an individual message."""
@ -98,151 +108,36 @@ class Console():
datetime = message.Message.datetime.strftime( datetime = message.Message.datetime.strftime(
'%A, %B %-d, %Y at %-H:%M %p UTC') '%A, %B %-d, %Y at %-H:%M %p UTC')
# Print the message # Print the message
self._write_output(f"") self.write_output(f"")
self._write_output(f"Message: {message.Message.id}") self.write_output(f"Message: {message.Message.id}")
self._write_output(f"Date: {datetime}") self.write_output(f"Date: {datetime}")
self._write_output(f"From: {message.Message.sender}") self.write_output(f"From: {message.Message.sender}")
self._write_output(f"To: {message.Message.recipient}") self.write_output(f"To: {message.Message.recipient}")
self._write_output(f"Subject: {message.Message.subject}") self.write_output(f"Subject: {message.Message.subject}")
self._write_output(f"") self.write_output(f"")
self._write_output(f"{message.Message.message}") self.write_output(f"{message.Message.message}")
def print_message_list(self, messages): def print_message_list(self, messages) -> None:
"""Print a list of messages.""" """Print a list of messages."""
# Print the column headers # Print the column headers
self._write_output(f"{'MSG#': <{5}} " self.write_output(f"{'MSG#': <{5}} "
f"{'TO': <{9}} " f"{'TO': <{9}} "
f"{'FROM': <{9}} " f"{'FROM': <{9}} "
f"{'DATE': <{11}} " f"{'DATE': <{11}} "
f"SUBJECT") f"SUBJECT")
# Print the messages # Print the messages
for message in messages: for message in messages:
datetime_ = message.Message.datetime.strftime('%Y-%m-%d') datetime_ = message.Message.datetime.strftime('%Y-%m-%d')
self._write_output(f"{message.Message.id: <{5}} " self.write_output(f"{message.Message.id: <{5}} "
f"{message.Message.recipient: <{9}} " f"{message.Message.recipient: <{9}} "
f"{message.Message.sender: <{9}} " f"{message.Message.sender: <{9}} "
f"{datetime_: <{11}} " f"{datetime_: <{11}} "
f"{message.Message.subject}") f"{message.Message.subject}")
# #
# Command functions # Command functions
# #
def bye(self, args):
"""Disconnect and exit."""
self._write_output("Bye!")
exit(0)
def delete(self, args):
"""Delete a message specified by ID number."""
if args.number:
try:
self.controller.delete(args)
self._write_output(f"Deleted message #{args.number}")
except Exception as e:
self._write_output(f"Message not found.")
def delete_mine(self, args):
"""Delete all messages addressed to the calling station's callsign."""
self._write_output("Delete all messages addressed to you? Y/N:")
response = sys.stdin.readline().strip()
if response.lower() != "y":
return
else:
try:
result = self.controller.delete_mine(args)
messages = result.all()
count = len(messages)
if count > 0:
self._write_output(f"Deleted {count} messages")
else:
self._write_output(f"No messages to delete.")
except Exception as e:
self._write_output(f"Unable to delete messages: {e}")
def heard(self, args):
"""Show a log of stations that have been heard by this station,
also known as the 'mheard' (linux) or 'jheard' (KPC, etc.) log.
"""
self._write_output(f"Heard stations:")
try:
result = self.controller.heard(args)
self._write_output(result.stdout)
except FileNotFoundError:
self._write_output(f"mheard utility not found.")
except Exception as e:
if self.config.debug:
raise
else:
self._write_output(f"Heard stations not available.")
def help(self, args):
self.parser.print_help()
def list(self, args):
"""List all public messages and private messages to the caller."""
result = self.controller.list(args)
self.print_message_list(result)
def list_mine(self, args):
"""List only messages addressed to the calling station's callsign,
including public and private messages.
"""
result = self.controller.list_mine(args)
self.print_message_list(result)
def read(self, args):
"""Read a message.
Arguments:
number -- the message number to read
"""
if args.number:
try:
result = self.controller.read(args)
self.print_message(result)
except sqlalchemy.exc.NoResultFound:
self._write_output(f"Message not found.")
except Exception as e:
print(e)
def read_mine(self, args):
"""Read all messages addressed to the calling station's callsign,
in sequence."""
result = self.controller.list_mine(args)
messages = result.all()
count = len(messages)
if count > 0:
self._write_output(f"Reading {count} messages:")
for message in messages:
self.print_message(message)
self._write_output("Enter to continue...")
sys.stdin.readline()
else:
self._write_output(f"No messages to read.")
def send(self, args, is_private=False):
"""Create a new message addressed to another callsign.
Required arguments:
callsign -- the recipient's callsign
Optional arguments:
subject -- message subject
message -- the message itself
"""
if not args.callsign:
args.callsign = self._read_line("Callsign:")
if not args.subject:
args.subject = self._read_line("Subject:")
if not args.message:
args.message = self._read_multiline(
"Message - end with /ex on a single line:")
try:
self.controller.send(args, is_private=is_private)
except Exception as e:
print(e)
def send_private(self, args): def send_private(self, args):
self.send(args, is_private=True) self.send(args, is_private=True)
"""Send a message visible only to the recipient callsign. """Send a message visible only to the recipient callsign.
@ -270,7 +165,7 @@ class Console():
self.print_greeting() self.print_greeting()
# Show initial prompt to the calling user # Show initial prompt to the calling user
self._write_output(self.config.command_prompt) self.write_output(self.config.command_prompt)
# Parse the BBS interactive commands for the rest of time # Parse the BBS interactive commands for the rest of time
for line in sys.stdin: for line in sys.stdin:
@ -284,4 +179,4 @@ class Console():
pass pass
# Show our prompt to the calling user again # Show our prompt to the calling user again
self._write_output(self.config.command_prompt) self.write_output(self.config.command_prompt)

View File

@ -93,17 +93,6 @@ class Controller():
except Exception: except Exception:
raise raise
def heard(self, args):
"""Show a log of stations that have been heard by this station,
also known as the 'mheard' (linux) or 'jheard' (KPC, etc.) log.
"""
try:
return subprocess.run(['mheard'], capture_output=True, text=True)
except FileNotFoundError:
raise
except Exception:
raise
def list(self, args): def list(self, args):
"""List all messages.""" """List all messages."""
with Session(self.engine) as session: with Session(self.engine) as session:

View File

@ -18,8 +18,6 @@
import argparse import argparse
from rsbbs.commands import Commands
class BBSArgumentParser(argparse.ArgumentParser): class BBSArgumentParser(argparse.ArgumentParser):
# Override the error handler to prevent spewing error cruft over the air # Override the error handler to prevent spewing error cruft over the air
@ -33,8 +31,8 @@ class BBSArgumentParser(argparse.ArgumentParser):
class Parser(BBSArgumentParser): class Parser(BBSArgumentParser):
def __init__(self, commands: Commands): def __init__(self):
self._init_parser(commands) self._init_parser()
# The only thing anyone should ever access from Parser is its parser # The only thing anyone should ever access from Parser is its parser
# attribute, so let's save everyone a step. # attribute, so let's save everyone a step.
@ -44,7 +42,7 @@ class Parser(BBSArgumentParser):
except AttributeError: except AttributeError:
return getattr(self.parser, attr) return getattr(self.parser, attr)
def _init_parser(self, commands): def _init_parser(self):
# Root parser for BBS commands # Root parser for BBS commands
self.parser = BBSArgumentParser( self.parser = BBSArgumentParser(
description='BBS Main Menu', description='BBS Main Menu',
@ -54,20 +52,9 @@ class Parser(BBSArgumentParser):
) )
# We will create a subparser for each individual command # We will create a subparser for each individual command
subparsers = self.parser.add_subparsers( self.subparsers = self.parser.add_subparsers(
title='Commands', title='Commands',
dest='command') dest='command')
# Loop through the commands and add each as a subparser # Plugins will then add a subparser for each command, so we're done
for name, aliases, help_msg, func, arguments in commands.commands: # here
# Add the command attributes
subparser = subparsers.add_parser(
name,
aliases=aliases,
help=help_msg,
)
# Add the command parameters
for arg_name, options in arguments.items():
subparser.add_argument(arg_name, **options)
# Trick to pass a function to call when the command is entered
subparser.set_defaults(func=func)

66
rsbbs/pluginloader.py Normal file
View File

@ -0,0 +1,66 @@
#!/usr/bin/env python
#
# Really Simple BBS - a really simple BBS for ax.25 packet radio.
# Copyright (C) 2023 John Burwell <john@atatdotdot.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import importlib
import os
class PluginLoader():
def __init__(self, api) -> None:
self.api = api
self.plugins = []
def load_plugins(self) -> None:
# Path to command plugin directory
plugins_dir = os.path.join(os.path.dirname(__file__), 'plugins')
# Discover all subdirectories in the plugins directory
plugin_dirs = [d
for d in os.listdir(plugins_dir)
if os.path.isdir(os.path.join(plugins_dir, d))
and not d.startswith('__')]
if self.api.config.debug:
print(f"Plugin dirs: {plugin_dirs}")
# Loop over each plugin directory
for plugin_dir in plugin_dirs:
try:
# Import the module containing the plugin class
if self.api.config.debug:
print(f"Import rsbbs.plugins.{plugin_dir}.plugin")
plugin_module = importlib.import_module(
f"rsbbs.plugins.{plugin_dir}.plugin")
# Get a reference to the plugin class
plugin_class = plugin_module.Plugin
# Initialize an instance of the plugin class, passing api as an
# argument
plugin = plugin_class(self.api)
# Add the loaded plugin to the list of plugins
self.plugins.append(plugin)
except Exception as e:
if self.api.config.debug:
print(f"{e}")
raise
else:
continue

View File

@ -0,0 +1,41 @@
#!/usr/bin/env python
#
# Really Simple BBS - a really simple BBS for ax.25 packet radio.
# Copyright (C) 2023 John Burwell <john@atatdotdot.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from rsbbs.console import Console
from rsbbs.parser import Parser
class Plugin():
def __init__(self, api: Console):
self.api = api
self.init_parser(api.parser)
if api.config.debug:
print(f"Plugin {__name__} loaded")
def init_parser(self, parser: Parser):
subparser = parser.subparsers.add_parser(
name='bye',
aliases=['b', 'q'],
help='Sign off and disconnect')
subparser.set_defaults(func=self.run)
def run(self, args):
"""Disconnect and exit."""
self.api.write_output("Bye!")
exit(0)

View File

@ -0,0 +1,47 @@
#!/usr/bin/env python
#
# Really Simple BBS - a really simple BBS for ax.25 packet radio.
# Copyright (C) 2023 John Burwell <john@atatdotdot.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from rsbbs.console import Console
from rsbbs.parser import Parser
class Plugin():
def __init__(self, api: Console):
self.api = api
self.init_parser(api.parser)
if api.config.debug:
print(f"Plugin {__name__} loaded")
def init_parser(self, parser: Parser):
subparser = parser.subparsers.add_parser(
name='delete',
aliases=['d', 'k'],
help='Delete a message')
subparser.add_argument('number',
help='The number of the message to delete')
subparser.set_defaults(func=self.run)
def run(self, args):
"""Delete a message specified by ID number."""
if args.number:
try:
self.api.controller.delete(args)
self.api.write_output(f"Deleted message #{args.number}")
except Exception as e:
self.api.write_output(f"Message not found.")

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python
#
# Really Simple BBS - a really simple BBS for ax.25 packet radio.
# Copyright (C) 2023 John Burwell <john@atatdotdot.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from rsbbs.console import Console
from rsbbs.parser import Parser
class Plugin():
def __init__(self, api: Console):
self.api = api
self.init_parser(api.parser)
if api.config.debug:
print(f"Plugin {__name__} loaded")
def init_parser(self, parser: Parser):
subparser = parser.subparsers.add_parser(
name='deletem',
aliases=['dm', 'km'],
help='Delete all messages addressed to you')
subparser.set_defaults(func=self.run)
def run(self, args):
"""Delete all messages addressed to the calling station's callsign."""
response = self.api.read_line(
"Delete all messages addressed to you? Y/N:")
if response.lower() != "y":
return
else:
try:
result = self.api.controller.delete_mine(args)
messages = result.all()
count = len(messages)
if count > 0:
self.api.write_output(f"Deleted {count} messages")
else:
self.api.write_output(f"No messages to delete.")
except Exception as e:
self.api.write_output(f"Unable to delete messages: {e}")

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python
#
# Really Simple BBS - a really simple BBS for ax.25 packet radio.
# Copyright (C) 2023 John Burwell <john@atatdotdot.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import subprocess
from rsbbs.console import Console
from rsbbs.parser import Parser
class Plugin():
def __init__(self, api: Console):
self.api = api
self.init_parser(api.parser)
if api.config.debug:
print(f"Plugin {__name__} loaded")
def init_parser(self, parser: Parser):
subparser = parser.subparsers.add_parser(
name='heard',
aliases=['j'],
help='Show heard stations log')
subparser.set_defaults(func=self.run)
def run(self, args):
"""Show a log of stations that have been heard by this station,
also known as the 'mheard' (linux) or 'jheard' (KPC, etc.) log.
"""
self.api.write_output(f"Heard stations:")
try:
result = subprocess.run(['mheard'], capture_output=True, text=True)
self.api.write_output(result.stdout)
except FileNotFoundError:
self.api.write_output(f"mheard utility not found.")
except Exception as e:
if self.api.config.debug:
raise
else:
self.api.write_output(f"Heard stations not available.")

View File

@ -0,0 +1,42 @@
#!/usr/bin/env python
#
# Really Simple BBS - a really simple BBS for ax.25 packet radio.
# Copyright (C) 2023 John Burwell <john@atatdotdot.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from rsbbs.console import Console
from rsbbs.parser import Parser
class Plugin():
def __init__(self, api: Console):
self.api = api
self.init_parser(api.parser)
if api.config.debug:
print(f"Plugin {__name__} loaded")
def init_parser(self, parser: Parser):
subparser = parser.subparsers.add_parser(
name='help',
aliases=['h', '?'],
help='Show help')
subparser.set_defaults(func=self.run)
def run(self, args):
"""Show a log of stations that have been heard by this station,
also known as the 'mheard' (linux) or 'jheard' (KPC, etc.) log.
"""
self.api.parser.print_help()

View File

@ -0,0 +1,41 @@
#!/usr/bin/env python
#
# Really Simple BBS - a really simple BBS for ax.25 packet radio.
# Copyright (C) 2023 John Burwell <john@atatdotdot.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from rsbbs.console import Console
from rsbbs.parser import Parser
class Plugin():
def __init__(self, api: Console):
self.api = api
self.init_parser(api.parser)
if api.config.debug:
print(f"Plugin {__name__} loaded")
def init_parser(self, parser: Parser):
subparser = parser.subparsers.add_parser(
name='list',
aliases=['l'],
help='List all available messages')
subparser.set_defaults(func=self.run)
def run(self, args):
"""List all public messages and messages private to the caller."""
result = self.api.controller.list(args)
self.api.print_message_list(result)

View File

@ -0,0 +1,43 @@
#!/usr/bin/env python
#
# Really Simple BBS - a really simple BBS for ax.25 packet radio.
# Copyright (C) 2023 John Burwell <john@atatdotdot.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from rsbbs.console import Console
from rsbbs.parser import Parser
class Plugin():
def __init__(self, api: Console):
self.api = api
self.init_parser(api.parser)
if api.config.debug:
print(f"Plugin {__name__} loaded")
def init_parser(self, parser: Parser):
subparser = parser.subparsers.add_parser(
name='listm',
aliases=['lm'],
help='List only messages addressed to you')
subparser.set_defaults(func=self.run)
def run(self, args):
"""List only messages addressed to the calling station's callsign,
including public and private messages.
"""
result = self.api.controller.list_mine(args)
self.api.print_message_list(result)

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python
#
# Really Simple BBS - a really simple BBS for ax.25 packet radio.
# Copyright (C) 2023 John Burwell <john@atatdotdot.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import sqlalchemy
from rsbbs.console import Console
from rsbbs.parser import Parser
class Plugin():
def __init__(self, api: Console):
self.api = api
self.init_parser(api.parser)
if api.config.debug:
print(f"Plugin {__name__} loaded")
def init_parser(self, parser: Parser):
subparser = parser.subparsers.add_parser(
name='read',
aliases=['r'],
help='Read a message')
subparser.add_argument('number', help='Message number to read')
subparser.set_defaults(func=self.run)
def run(self, args):
"""Read a message.
Arguments:
number -- the message number to read
"""
if args.number:
try:
result = self.api.controller.read(args)
self.api.print_message(result)
except sqlalchemy.exc.NoResultFound:
self.api.write_output(f"Message not found.")
except Exception as e:
print(e)

View File

@ -0,0 +1,52 @@
#!/usr/bin/env python
#
# Really Simple BBS - a really simple BBS for ax.25 packet radio.
# Copyright (C) 2023 John Burwell <john@atatdotdot.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import sqlalchemy
from rsbbs.console import Console
from rsbbs.parser import Parser
class Plugin():
def __init__(self, api: Console):
self.api = api
self.init_parser(api.parser)
if api.config.debug:
print(f"Plugin {__name__} loaded")
def init_parser(self, parser: Parser):
subparser = parser.subparsers.add_parser(
name='readm',
aliases=['rm'],
help='Read all messages addressed to you')
subparser.set_defaults(func=self.run)
def run(self, args):
"""Read all messages addressed to the calling station's callsign,
in sequence."""
result = self.api.controller.list_mine(args)
messages = result.all()
count = len(messages)
if count > 0:
self.api.write_output(f"Reading {count} messages:")
for message in messages:
self.api.print_message(message)
self.api.read_enter("Enter to continue...")
else:
self.api.write_output(f"No messages to read.")

View File

@ -0,0 +1,63 @@
#!/usr/bin/env python
#
# Really Simple BBS - a really simple BBS for ax.25 packet radio.
# Copyright (C) 2023 John Burwell <john@atatdotdot.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import sqlalchemy
from rsbbs.console import Console
from rsbbs.parser import Parser
class Plugin():
def __init__(self, api: Console):
self.api = api
self.init_parser(api.parser)
if api.config.debug:
print(f"Plugin {__name__} loaded")
def init_parser(self, parser: Parser):
subparser = parser.subparsers.add_parser(
name='send',
aliases=['s'],
help='Send a new message to a user')
subparser.add_argument('--callsign', help='Message recipient callsign')
subparser.add_argument('--subject', help='Message subject')
subparser.add_argument('--message', help='Message')
subparser.set_defaults(func=self.run)
def run(self, args):
"""Create a new message addressed to another callsign.
Required arguments:
callsign -- the recipient's callsign
Optional arguments:
subject -- message subject
message -- the message itself
"""
if not args.callsign:
args.callsign = self.api.read_line("Callsign:")
if not args.subject:
args.subject = self.api.read_line("Subject:")
if not args.message:
args.message = self.api.read_multiline(
"Message - end with /ex on a single line:")
try:
self.api.controller.send(args, is_private=False)
except Exception as e:
print(e)

View File

@ -0,0 +1,63 @@
#!/usr/bin/env python
#
# Really Simple BBS - a really simple BBS for ax.25 packet radio.
# Copyright (C) 2023 John Burwell <john@atatdotdot.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import sqlalchemy
from rsbbs.console import Console
from rsbbs.parser import Parser
class Plugin():
def __init__(self, api: Console):
self.api = api
self.init_parser(api.parser)
if api.config.debug:
print(f"Plugin {__name__} loaded")
def init_parser(self, parser: Parser):
subparser = parser.subparsers.add_parser(
name='sendp',
aliases=['sp'],
help='Send a private message to a user')
subparser.add_argument('--callsign', help='Message recipient callsign')
subparser.add_argument('--subject', help='Message subject')
subparser.add_argument('--message', help='Message')
subparser.set_defaults(func=self.run)
def run(self, args):
"""Create a new message addressed to another callsign.
Required arguments:
callsign -- the recipient's callsign
Optional arguments:
subject -- message subject
message -- the message itself
"""
if not args.callsign:
args.callsign = self.api.read_line("Callsign:")
if not args.subject:
args.subject = self.api.read_line("Subject:")
if not args.message:
args.message = self.api.read_multiline(
"Message - end with /ex on a single line:")
try:
self.api.controller.send(args, is_private=True)
except Exception as e:
print(e)

View File

@ -20,7 +20,6 @@ import argparse
import sys import sys
from rsbbs import __version__ from rsbbs import __version__
from rsbbs.commands import Commands
from rsbbs.config import Config from rsbbs.config import Config
from rsbbs.console import Console from rsbbs.console import Console
from rsbbs.controller import Controller from rsbbs.controller import Controller
@ -82,7 +81,7 @@ def main():
app_name='rsbbs', app_name='rsbbs',
args=argv_args) args=argv_args)
# Init the contoller # Init the controller
controller = Controller(config) controller = Controller(config)
# Init the UI console # Init the UI console