An open API service indexing awesome lists of open source software.

https://github.com/klemek/miniscord

A minimalist discord bot API 🤖
https://github.com/klemek/miniscord

discord python3

Last synced: 2 months ago
JSON representation

A minimalist discord bot API 🤖

Awesome Lists containing this project

README

        

![Python Version >= 3.7](https://img.shields.io/badge/python-%3E=3.7%20-blue)
[![Scc Count Badge](https://sloc.xyz/github/klemek/miniscord?category=code)](https://github.com/boyter/scc/#badges-beta)
[![Total alerts](https://img.shields.io/lgtm/alerts/g/Klemek/miniscord.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Klemek/miniscord/alerts/)
[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/Klemek/miniscord.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Klemek/miniscord/context:python)
[![Coverage Status](https://coveralls.io/repos/github/Klemek/miniscord/badge.svg?branch=master)](https://coveralls.io/github/Klemek/miniscord?branch=master)

# Miniscord
*A minimalist discord bot API*

```python
from miniscord import Bot
import discord

async def hello(client: discord.client, message: discord.Message, *args: str):
await message.channel.send("Hello!")

bot = Bot(
"test-app", # name
"0.1-alpha", # version
alias="|" # respond to '|command' messages
)
bot.register_command(
"hello", # command text (regex)
hello, # command function
"hello: says 'Hello!'", # short help
"```\n" # long help
"* |help\n"
"\tSays 'Hello!'.\n"
"```"
)
bot.start() # this bot respond to "|help", "|info" and "|hello"
```

![](./sample.jpg)

> **âš  Disclaimer:** I intend to use this project personally, I'm open to ideas but I don't care if it doesn't work for you. Same for the name, feel free to use it, I'm not registering it on PyPI

- [Features](#features)
- [Installation](#installation)
- [Documentation](#documentation)
- [Bot init and launch](#bot-init-and-launch)
- [Bot configuration properties](#bot-configuration-properties)
- [Registering commands](#registering-commands)
- [Registering fallback](#registering-fallback)
- [Registering watcher](#registering-watcher)
- [Game status](#game-status)
- [Exposed utility functions](#exposed-utility-functions)
- [`delete_message`](#deletemessage)
- [`channel_id` / `sender_id`](#channelid-senderid)
- [Full example](#full-example)
- [Versions](#versions)
- [TODO](#todo)

## Features

* Easy command registration
* Automatic help message
* Arguments splitting
* Message watcher registration
* Message handling configuration
* Static and dynamic custom game statuses

## Installation

**1. Install package**

```
pip install git+git://github.com/Klemek/miniscord.git
```

**2. Make a .env file as following**

```
#.env
DISCORD_TOKEN=
```

## Documentation

### Bot init and launch

```python
bot = Bot(
"test-app", # name
"0.1-alpha", # version
alias="|" # respond to '|command' messages
)

# configure bot and register commands

bot.start() # blocking function
```

### Bot configuration properties

* `token_env_var` (default: `"DISCORD_TOKEN"`)
* Which var to read in the `.env` file.
* `remove_mentions` (default: `False`)
* Remove any mention in the message / arguments.
* `any_mention` (default: `False`)
* If the bot respond to a mention in the middle of messages.
* `log_calls` (default: `False`)
* Log any calls to the Python logging.
* `guild_logs_file` (default: `"guilds.log"`)
* Log guilds join/leave on a file.
* `enforce_write_permission` (default: `True`)
* If the bot can't respond on a channel it was called, it sends a DM to the caller.
* `lower_command_names` (default: `True`)
* Use lowercase on command names (if false, commands are case-sensitive).
* `game_change_delay` (default: `10`)
* Change the game status every n seconds.
* `error_restart_delay` (default: `2`)
* On crash, restart after n seconds.
* `answer` (default: `True`)
* Use the answer capability on `help` and `info` functions
* `answer_mention` (default: `True`)
* Mention author in the answer

### Registering commands

Register a custom function to be called on a certain keyword.

> Note : You can override the "help" and "info" commands by your own.

```python
async def hello(client: discord.client, message: discord.Message, *args: str):
'''
Parameters
----------
client : discord.client
Discord object used by the bot to interact with Discord API
(see Discord documentation)
message : discord.Message
Discord object describing the message received via the Discord API
(see Discord documentation)
*args : str
The message viewed as string arguments like a CLI
(argument 0 will always be the command itself)
'''
if len(args) > 1:
await message.channel.send(f"Hello {args[1]}!")
else :
await message.channel.send("Hello stranger!")

bot.register_command(
"hello", # command text (regex)
hello, # command function
"hello: says 'Hello!'", # short help
f"```\n" # long help
f"* |help\n"
f"\tSays 'Hello!'.\n"
f"```"
)
```

### Registering fallback

Register a custom function to be called when the bot is mentioned but no command was found.

```python
async def mention(client: discord.client, message: discord.Message, *args: str):
"""See 'Registering commands' for arguments description"""
await message.channel.send(f"Did you mention me {message.author.mention}?")

bot.register_fallback(mention) # the bot was mentioned or the alias was used
```

### Registering watcher

Register a custom function to be called on every messages except this bot own messages.

```python
async def message(client: discord.client, message: discord.Message):
"""See 'Registering commands' for arguments description"""
if "catapult" on message.content:
await message.channel.send(f"No profanity on this server")

bot.register_watcher(message) # any message was sent (except this bot messages)
```

### Registering events

Register a [discord API event](https://discordpy.readthedocs.io/en/latest/api.html#discord-api-events)

The function must be exactly named after the event

```python
async def on_ready() -> bool:
print("on_ready")
return True # if False is returned, prevent miniscord's handling of the event

bot.register_event(on_ready)
```

### Game status

On starting, the bot will cycle through 2-3 "game status" (under its name as "playing xxx") :

* `v`: current version registered in code
* ` guilds`: number of connected guilds
* `help`: command for help (if an alias is registered)

It will change its status every `game_change_delay` (default as 10 seconds).

You can add custom game statuses like this :

```python
bot.games += ["My custom message"] # static message

value = 0
bot.games += [lambda: f"value:{value}"] # dynamic message, computed at change
```

You can also remove the pre-existing statuses :

```python
bot.games = ["My custom message"]
```

### Exposed utility functions

Along with the bot are exposed several utility functions.

#### `delete_message`

Delete the given Discord message from the channel. Return `True` if the message was deleted successfully.

```python
from miniscord import delete_message
import asyncio

async def message(client: discord.client, message: discord.Message):
if "catapult" in message.content:
sent_msg = await message.channel.send(f"{message.author.mention} No profanity on this server")
if await delete_message(message):
await asyncio.sleep(5)
await delete_message(sent_msg)
```

#### `channel_id` / `sender_id`

Helps identify where the discussion is happening (Might be used as a key in a state dictionary)

An user on multiple channels will have a unique id on each.

```python
from miniscord import channel_id, sender_id
from collections import defaultdict

channels = defaultdict(lambda:0)
senders = defaultdict(lambda:0)

async def mention(client: discord.client, message: discord.Message, *args: str):
channels[channel_id(message)] += 1
senders[sender_id(message)] += 1
await message.channel.send(
f"{message.author.mention} mentioned me {senders[sender_id(message)]} times on this channel\n"
f"{channels[channel_id(message)]} mentions on this channel so far"
)
```

## Full example

```Python
import logging
import discord
import asyncio
from collections import defaultdict
from miniscord import Bot, delete_message, channel_id, sender_id

channels = defaultdict(lambda:0)
senders = defaultdict(lambda:0)

logging.basicConfig(format="[%(asctime)s][%(levelname)s][%(module)s] %(message)s", level=logging.INFO)

async def hello(client: discord.client, message: discord.Message, *args: str):
'''
Parameters
----------
client : discord.client
Discord object used by the bot to interact with Discord API
(see Discord documentation)
message : discord.Message
Discord object describing the message received via the Discord API
(see Discord documentation)
*args : str
The message viewed as string arguments like a CLI
(argument 0 will always be the command itself)
'''
if len(args) > 1:
await message.channel.send(f"Hello {args[1]}!")
else:
await message.channel.send("Hello stranger!")

async def mention(client: discord.client, message: discord.Message, *args: str):
channels[channel_id(message)] += 1
senders[sender_id(message)] += 1
await message.channel.send(
f"{message.author.mention} mentioned me {senders[sender_id(message)]} times on this channel\n"
f"{channels[channel_id(message)]} mentions on this channel so far"
)

async def message(client: discord.client, message: discord.Message):
if "catapult" in message.content:
sent_msg = await message.channel.send(f"{message.author.mention} No profanity on this server")
if await delete_message(message): # False if "manage messages" permission not allowed
await asyncio.sleep(5)
await delete_message(sent_msg)

bot = Bot(
"test-app", # name
"0.1-alpha", # version
alias="|" # respond to '|command' messages
)

bot.register_command(
"hello", # command text (regex)
hello, # command function
"hello: says 'Hello!'", # short help
"```\n" # long help
"* |help\n"
"\tSays 'Hello!'.\n"
"```"
)

bot.register_fallback(mention) # the bot was mentioned or the alias was used

bot.register_watcher(message) # any message was sent (except this bot messages)

bot.games = [lambda:f"{sum(senders.values())} mentions"] # custom dynamic game status
bot.game_change_delay = 1 # refresh game status every second

bot.start() # this bot respond to "|help", "|info" and "|hello"
```

## Versions

* v0.1.0 : Discord v2 API with intents
* v0.0.3 : custom events handling
* v0.0.2 : new answer capability
* v0.0.1 : initial version

## TODO

* Expose more events
* Write more tests
* Add comments to code
* Separate branches
* Working CI
* Fix bugs