Added example mod to README
parent
0801762f07
commit
78290554bd
76
README.md
76
README.md
|
@ -1,2 +1,76 @@
|
|||
# Frank
|
||||
Frank is a modular tool for quickly creating Discord bots.
|
||||
## Description
|
||||
Playing around with creating a Discord bot is a fun pass-time, and a good way to learn a programming language. Sadly,
|
||||
however, discord.py can be a little hard to work with at times. That's when I got the idea to create Frank. The goal of
|
||||
Frank is to make creating Discord bots easier. It handles all the bot-related stuff in the background, so you can focus
|
||||
on writing the functionality of the bot itself, not how the bot works/interacts with Discord.
|
||||
|
||||
Frank works by dividing the bot into modules. Each module has its own prefix, commands, and daemons. Frank handles
|
||||
routing the Discord commands to their respective functions.
|
||||
|
||||
## Example Module
|
||||
In this section, I've written an example module for you, to understand the basic mechanics behind Frank.
|
||||
|
||||
```python
|
||||
import frank
|
||||
|
||||
class ExampleMod(frank.Module):
|
||||
PREFIX = 'examp'
|
||||
NAME = 'example'
|
||||
HELP = 'an example module'
|
||||
```
|
||||
|
||||
This first part shows the three important variables in any module.
|
||||
- PREFIX defines the string used to use the commands defined in this module. This means you can use the module as such
|
||||
inside your Discord server:
|
||||
```
|
||||
fr examp [NAME_OF_COMMAND] [ARGS]
|
||||
```
|
||||
With fr being the default prefix for Frank (can be overwritten). As you define more modules, they should all have a
|
||||
unique prefix. This is how Frank's modular system works, and any modules added to the list will automatically be
|
||||
picked up by Frank. The PREFIX value can also be list, allowing for multiple prefixes: for example a long,
|
||||
description one, and a short, easy to type one (e.g. minecraft and mc).
|
||||
|
||||
```python
|
||||
def pre_start(self):
|
||||
self.some_var = 'a value needed for working'
|
||||
```
|
||||
The pre_start function is where you define any variables which should be created before any daemons are started or
|
||||
commands are run. I don't recommend overwriting `__init__`, as this might break compatibility with future versions of
|
||||
Frank.
|
||||
|
||||
```python
|
||||
@frank.command('command', help_str='a small description of the command')
|
||||
async def some_command(self, cmd, author, channel, mid):
|
||||
# do some stuff
|
||||
pass
|
||||
|
||||
@frank.daemon()
|
||||
async def some_daemon(self):
|
||||
while True:
|
||||
# do some stuff
|
||||
pass
|
||||
|
||||
@frank.default()
|
||||
async def default_cmd(prefix, author, channel, mid):
|
||||
# do some default action
|
||||
pass
|
||||
```
|
||||
|
||||
These three decorators are the bread and butter of Frank. Let's break them down:
|
||||
- `frank.command` defines a command. The first argument is its keyword, which will be used to execute the command. The
|
||||
help_str value is used in the help command, to show some information about the module. The syntax is the same as
|
||||
before:
|
||||
```
|
||||
fr examp command [ARGS]
|
||||
```
|
||||
This is how you can define as many Discord commands as you want, without needing to know how to parse the messages
|
||||
etc. Each command gets the `author`, `channel`, and `id` of the message. The `cmd` variable contains all the arguments passed
|
||||
to the command.
|
||||
- `frank.daemon` defines a daemon, a process that should run in the background for as long as the bot is active. It
|
||||
should contain a while loop and preferably a sleep function using `asyncio.sleep()` (there are plans to improve this
|
||||
behavior). Because a daemon is just a method of the module class, it has access to all class variables, including
|
||||
those defined in `pre_start`.
|
||||
- `frank.default` defines the command that should be run if the module is called without explicitely giving a command.
|
||||
For example, if you call `fr examp` without specifying a command, it will run the default command. This is useful for
|
||||
making a command that's used very often easier to execute.
|
||||
|
|
|
@ -17,6 +17,7 @@ if TYPE_CHECKING:
|
|||
|
||||
class ModuleMeta:
|
||||
def _filter_attrs(self, condition: callable[[Any], bool]) -> List[Any]:
|
||||
# This prevents an infinite loop of getting the attribute
|
||||
illegal_names = ['commands', 'daemons', 'default']
|
||||
|
||||
output = []
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
jedi~=0.17.2
|
||||
jedi>=0.17.2,<1.0.0
|
||||
flake8~=3.8.3
|
||||
flake8-bugbear~=20.1.4
|
||||
flake8-builtins~=1.5.3
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
from frank import Module, command, default
|
||||
from frank import Module, command, default, daemon
|
||||
|
||||
|
||||
class ModuleTester(Module):
|
||||
PREFIX = 'tester'
|
||||
|
||||
@command('test')
|
||||
async def test(cmd, author, channel, mid):
|
||||
pass
|
||||
|
@ -9,3 +11,7 @@ class ModuleTester(Module):
|
|||
@default()
|
||||
async def test2(prefix, cmd, author, channel, mid):
|
||||
pass
|
||||
|
||||
@daemon()
|
||||
async def test_daemon(self):
|
||||
pass
|
||||
|
|
|
@ -5,6 +5,10 @@ import pytest
|
|||
|
||||
|
||||
def test_property_types():
|
||||
"""
|
||||
Test wether the cached_property's return the expected value
|
||||
"""
|
||||
|
||||
test_mod = ModuleTester(None)
|
||||
|
||||
assert isinstance(test_mod.default, Default)
|
||||
|
@ -15,7 +19,7 @@ def test_property_types():
|
|||
isinstance(item, Command) for item in test_mod.commands
|
||||
))
|
||||
|
||||
assert isinstance(test_mod.commands, list)
|
||||
assert isinstance(test_mod.daemons, list)
|
||||
assert all((
|
||||
isinstance(item, Daemon) for item in test_mod.daemons
|
||||
))
|
||||
|
@ -28,3 +32,10 @@ async def test_invalid_command():
|
|||
with pytest.raises(InvalidCommand):
|
||||
# None is just a placeholder here
|
||||
await test_mod('aninvalidcommand', None, None, None)
|
||||
|
||||
|
||||
def test_match():
|
||||
test_mod = ModuleTester(None)
|
||||
|
||||
assert test_mod.match('tester')
|
||||
assert not test_mod.match('testerrr')
|
||||
|
|
Reference in New Issue