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
# 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"
# 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
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import os
import sys
import sqlalchemy.exc
import rsbbs
from rsbbs.commands import Commands
from rsbbs.config import Config
from rsbbs.controller import Controller
from rsbbs.parser import Parser
from rsbbs.pluginloader import PluginLoader
# Main UI console class
@ -35,33 +36,42 @@ class Console():
self.config = config
self.controller = controller
self.commands = Commands(self)
self.parser = Parser(self.commands)
self.parser = Parser()
self.pluginloader = PluginLoader(self)
self.pluginloader.load_plugins()
#
# 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,
until we get something.
"""
output = None
while not output:
if prompt:
self._write_output(prompt)
self.write_output(prompt)
input = sys.stdin.readline().strip()
if input != "":
output = input
return output
def _read_multiline(self, prompt):
def read_multiline(self, prompt) -> str:
"""Read multiple lines of input, with an optional prompt,
until the user enters '/ex' by itself on a line.
"""
output = []
if prompt:
self._write_output(prompt)
self.write_output(prompt)
while True:
line = sys.stdin.readline()
if line.lower().strip() == "/ex":
@ -70,14 +80,14 @@ class Console():
output.append(line)
return ''.join(output)
def _write_output(self, output):
def write_output(self, output) -> None:
"""Write something to stdout."""
sys.stdout.write(output + '\r\n')
def print_configuration(self):
self._write_output(repr(self.config))
def print_configuration(self) -> None:
self.write_output(repr(self.config))
def print_greeting(self):
def print_greeting(self) -> None:
# Show greeting
greeting = []
greeting.append(f"[RSBBS-{rsbbs.__version__}] listening on "
@ -90,7 +100,7 @@ class Console():
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):
"""Print an individual message."""
@ -98,151 +108,36 @@ class Console():
datetime = message.Message.datetime.strftime(
'%A, %B %-d, %Y at %-H:%M %p UTC')
# Print the message
self._write_output(f"")
self._write_output(f"Message: {message.Message.id}")
self._write_output(f"Date: {datetime}")
self._write_output(f"From: {message.Message.sender}")
self._write_output(f"To: {message.Message.recipient}")
self._write_output(f"Subject: {message.Message.subject}")
self._write_output(f"")
self._write_output(f"{message.Message.message}")
self.write_output(f"")
self.write_output(f"Message: {message.Message.id}")
self.write_output(f"Date: {datetime}")
self.write_output(f"From: {message.Message.sender}")
self.write_output(f"To: {message.Message.recipient}")
self.write_output(f"Subject: {message.Message.subject}")
self.write_output(f"")
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 the column headers
self._write_output(f"{'MSG#': <{5}} "
f"{'TO': <{9}} "
f"{'FROM': <{9}} "
f"{'DATE': <{11}} "
f"SUBJECT")
self.write_output(f"{'MSG#': <{5}} "
f"{'TO': <{9}} "
f"{'FROM': <{9}} "
f"{'DATE': <{11}} "
f"SUBJECT")
# Print the messages
for message in messages:
datetime_ = message.Message.datetime.strftime('%Y-%m-%d')
self._write_output(f"{message.Message.id: <{5}} "
f"{message.Message.recipient: <{9}} "
f"{message.Message.sender: <{9}} "
f"{datetime_: <{11}} "
f"{message.Message.subject}")
self.write_output(f"{message.Message.id: <{5}} "
f"{message.Message.recipient: <{9}} "
f"{message.Message.sender: <{9}} "
f"{datetime_: <{11}} "
f"{message.Message.subject}")
#
# 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):
self.send(args, is_private=True)
"""Send a message visible only to the recipient callsign.
@ -270,7 +165,7 @@ class Console():
self.print_greeting()
# 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
for line in sys.stdin:
@ -284,4 +179,4 @@ class Console():
pass
# 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:
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):
"""List all messages."""
with Session(self.engine) as session:

View File

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

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
from rsbbs import __version__
from rsbbs.commands import Commands
from rsbbs.config import Config
from rsbbs.console import Console
from rsbbs.controller import Controller
@ -82,7 +81,7 @@ def main():
app_name='rsbbs',
args=argv_args)
# Init the contoller
# Init the controller
controller = Controller(config)
# Init the UI console