Skip to content

Commit

Permalink
add tests and refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Two-Play committed Oct 27, 2024
1 parent 124943b commit a740d29
Show file tree
Hide file tree
Showing 11 changed files with 395 additions and 109 deletions.
50 changes: 38 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@
# Crafty Controller Discord bot

![GitHub License](https://img.shields.io/github/license/Two-Play/Crafty-Discord-bot)
![GitHub top language](https://img.shields.io/github/languages/top/Two-Play/Crafty-Discord-bot)
![GitHub contributors](https://img.shields.io/github/contributors/Two-Play/Crafty-Discord-bot)
![GitHub Release Date](https://img.shields.io/github/release-date/Two-Play/Crafty-Discord-bot)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/d5b3f979005e4c52916f7fb741068483)](https://app.codacy.com/gh/Two-Play/Crafty-Discord-bot/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[![Codacy Badge](https://app.codacy.com/project/badge/Coverage/d5b3f979005e4c52916f7fb741068483)](https://app.codacy.com/gh/Two-Play/Crafty-Discord-bot/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage)

## Table of Contents

1. [Introduction](#introduction)
2. [Features](#features)
3. [Roadmap](#roadmap)
4. [Installation](#installation)
- [Requirements](#requirements)
- [Docker](#docker)
- [Python](#python)
5. [Usage](#usage)
6. [Issues](#issues)
7. [Contributing](#contributing)
8. [Support the Project](#support-the-project)
9. [Donations](#donations)
10. [License](#license)

## Introduction

This is a Discord bot that is designed to control the Crafty-Controller-4 server. This is useful if friends want to start a server and you want to control it from Discord.
The bot is written in Python and uses the Discord.py library to interact with the
Discord API.

## Table of Contents
### Features

- **Server Status**: Get the status of the server
- **Server Start**: Start the server
- **Server Stop**: Stop the server

## Roadmap

- **Server Restart**: Restart the server
- **Server Backup**: Create a backup of the server
- **Web UI**: Create a web interface for the bot

1. [Introduction](#introduction)
2. [Installation](#installation)
- [Requirements](#requirements)
- [Docker](#docker)
- [Python](#python)
3. [Usage](#usage)
4. [Issues](#issues)
5. [Contributing](#contributing)
6. [Support the Project](#support-the-project)
7. [Donations](#donations)
8. [License](#license)


## Installation
Expand Down Expand Up @@ -56,6 +74,7 @@ server and obtain the user token. You can do this by following these steps:
9. Save your user token in a safe place (you will need it later)

#### Discord Bot

You will need to create a new Discord bot and obtain a bot token. You can do this by following these steps:

1. Go to the [Discord Developer Portal](https://discord.com/developers/applications)
Expand Down Expand Up @@ -88,6 +107,7 @@ You will need to create a new Discord bot and obtain a bot token. You can do thi
Congratulations! Your bot has been invited to your server

### Docker

Installing the bot using Docker is the easiest way to get started. To do this, you will need to have Docker installed on your system. If you do not have Docker installed, you can download it from the [official Docker website](https://www.docker.com/get-started).


Expand Down Expand Up @@ -117,6 +137,7 @@ services:
```
### Python
If you would like to install the bot using Python, you will need to have Python 3.8 or higher installed on your system.
Clone the repository
Expand Down Expand Up @@ -168,13 +189,15 @@ python main.py
Replace `YOUR_DISCORD_TOKEN` with your Discord bot token and `YOUR_MONGO_URI` with your MongoDB connection string.

## Usage

### Slash Commands (Beta)
The bot supports slash commands. To use the slash commands, you will need to have the `Use slash commands` permission enabled for the bot.
```bash
/help or /bot_help
```

### Command (>)

Enter the following command to get a list of available commands:
```bash
>help or >bot_help
Expand Down Expand Up @@ -204,6 +227,7 @@ Thank you for your contribution!
If you have any questions, please feel free to reach out to us

## Support the Project

If you would like to support the project, you can do so by:

- Giving the project a star on GitHub
Expand All @@ -212,6 +236,7 @@ If you would like to support the project, you can do so by:
- Donating to the project

## Donations

If you would like to donate to the project, you can do so using the following methods:

| Platform | Link | QR Code |
Expand All @@ -232,4 +257,5 @@ You can click on the QR code to show a larger version of the QR code. GitHub doe
| ![Monero Badge](https://img.shields.io/badge/Monero-F60?logo=monero&logoColor=fff&style=for-the-badge) | XMR | ![Monero QR Code](https://api.qrserver.com/v1/create-qr-code/?color=000000&bgcolor=FFFFFF&data=41hZYQV5uDzfiLCusRAxARST3hfTzGv7RNRyB92G1RZw64pvEQqwDo94zZHVxfvcmncLU1ockvJxbZBQToPqqDtBAor97sU&qzone=1&margin=0&size=150x150&ecc=L) | `41hZYQV5uDzfiLCusRAxARST3hfTzGv7RNRyB92G1RZw64pvEQqwDo94zZHVxfvcmncLU1ockvJxbZBQToPqqDtBAor97sU` |

## License

This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.
8 changes: 8 additions & 0 deletions core/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Network
# HTTP status code for a successful request
STATUS_SUCCESS = 200

# Main
API_ENDPOINT = '/api/v2/servers/'
GUILD_ID = 1168172802562601121
AUTO_STOP_SLEEP = 1800 # 30 minutes
1 change: 0 additions & 1 deletion core/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import os
import sys


def check_env_vars():
"""
Check if all required environment variables are set and exit if any are missing.
Expand Down
17 changes: 17 additions & 0 deletions core/imports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# core/imports.py

import asyncio
import os
import requests
import discord
from discord.ext import commands
from discord import Interaction, app_commands
from discord.ext.commands import is_owner
from discord import ui
from dotenv import load_dotenv

# Custom imports
from core.helper import check_env_vars
from core.network import is_response_successful, get_json_response
from core.printing import print_server_info, print_server_status
from core.server import is_server_running, stop_server, get_player_count
119 changes: 46 additions & 73 deletions core/main.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
"""
This is the main file of the bot. It contains the main loop and the event handlers.
"""

import asyncio
import os
import requests
import discord
from discord.ext import commands
from discord import Interaction, app_commands
from discord import app_commands
from discord.ext.commands import is_owner
from discord import ui
from dotenv import load_dotenv

from helper import check_env_vars
from network import is_response_successful, get_json_response
from printing import print_server_info, print_server_status
from core.constants import AUTO_STOP_SLEEP, GUILD_ID, API_ENDPOINT
from core.helper import check_env_vars
from core.network import get_json_response
from core.printing import print_server_info, print_server_status
from core.server import is_server_running, stop_server, get_player_count

# Load environment variables from .env file
load_dotenv()
Expand All @@ -20,43 +24,16 @@
if 'USERNAME' in os.environ and 'PASSWORD' in os.environ:
USERNAME = os.environ['USERNAME']
PASSWORD = os.environ['PASSWORD']
API_ENDPOINT = '/api/v2/servers/'
GUILD_ID = 1168172802562601121
AUTO_STOP_SLEEP = 1800 # 30 minutes


intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix='>', intents=intents)

def stop_server(server_id) -> bool:
data = get_json_response(API_ENDPOINT + str(server_id) + '/action/stop_server', 'failed to stop server')
if data['status'] == "ok":
print('Server stopped', str(server_id))
return True
else:
print('failed to stop server')
return False

def get_player_count(server_id, ctx=None) -> int:
data = get_json_response(API_ENDPOINT + str(server_id) + '/stats', 'failed to get server stats')
if not data:
if ctx:
ctx.reply('failed to get server stats')
return -1
return data['data']['online']

def is_server_running(server_id, ctx=None) -> bool:
data = get_json_response(API_ENDPOINT + str(server_id) + '/stats', 'failed to get server stats')
if not data:
if ctx:
ctx.reply('failed to get server stats')
return False
return data['data']['running']


# stop server after 1 hour of inactivity (no players online)
async def auto_stop():
"""
Automatically stop servers that have been inactive for a certain period of time.
"""
while True:
# get list of servers ids and loop through them and add only the ones that are running
data = get_json_response('/api/v2/servers', 'failed to get server list')
Expand All @@ -81,21 +58,21 @@ async def on_ready():
# get_token()


"""
@bot.tree.command(name="rps")/*
@app_commands.guilds(discord.Object(id=GUILD_ID))@app_commands.choices(choices=[
app_commands.Choice(name="Rock", value="rock"),
app_commands.Choice(name="Paper", value="paper"),
app_commands.Choice(name="Scissors", value="scissors"),
])
async def rps(i: discord.Interaction, choices: app_commands.Choice[str]):
if (choices.value == 'rock'):
counter = 'paper'
elif (choices.value == 'paper'):
counter = 'scissors'
else:
counter = 'rock'
"""

# @bot.tree.command(name="rps")/*
# @app_commands.guilds(discord.Object(id=GUILD_ID))@app_commands.choices(choices=[
# app_commands.Choice(name="Rock", value="rock"),
# app_commands.Choice(name="Paper", value="paper"),
# app_commands.Choice(name="Scissors", value="scissors"),
# ])
# async def rps(i: discord.Interaction, choices: app_commands.Choice[str]):
# if (choices.value == 'rock'):
# counter = 'paper'
# elif (choices.value == 'paper'):
# counter = 'scissors'
# else:
# counter = 'rock'



@bot.hybrid_command(name='sync', description='Sync commands')
Expand All @@ -112,32 +89,24 @@ async def sync(ctx) -> None:
@is_owner()
async def get_token(ctx):
print('get_token')
response = requests.post(SERVER_URL + '/api/v2/auth/login', json={'username': USERNAME, 'password': PASSWORD},
verify=False)
if is_response_successful(response):
os.environ['CRAFTY_TOKEN'] = response.json()['data']['token']
await ctx.send('Toke successful retrieved')
return True
else:
print('Login failed', response.status_code)
return False

data = get_json_response('/api/v2/auth/login', 'failed to get token')
if not data:
await ctx.reply('failed to get token')
return

os.environ['CRAFTY_TOKEN'] = data['data']['token']
await ctx.send('Toke successful retrieved')

@bot.hybrid_command(name='list', description='get server list')
@app_commands.guilds(discord.Object(id=GUILD_ID))# get the list of servers
async def get_list(ctx):
print('servers')
data = get_json_response('/api/v2/servers', 'failed to get server list')
server_info_text = print_server_info(data)

response = requests.get(SERVER_URL + '/api/v2/servers',
headers={'Authorization': 'Bearer ' + os.environ['CRAFTY_TOKEN']}, verify=False)
if is_response_successful(response):
# print(json.dumps(response.json(), indent=4))
await ctx.reply(f"Serverinformationen:\n{server_info_text}")

data = response.json()
server_info_text = print_server_info(data)

await ctx.reply(f"Serverinformationen:\n{server_info_text}")
else:
await ctx.reply('failed to get server list')


# get statistics of a server
Expand Down Expand Up @@ -169,7 +138,8 @@ async def start(ctx, server_id):
await ctx.reply('Server already running')
return

data = get_json_response(API_ENDPOINT + str(server_id) + '/action/start_server', 'failed to start server')
data = get_json_response(API_ENDPOINT + str(server_id) + '/action/start_server',
'failed to start server')
if not data:
await ctx.reply('failed to start server')
return
Expand All @@ -192,7 +162,7 @@ async def stop(ctx, server_id):
# check if player is online
player_count = get_player_count(server_id, ctx)
if player_count != 0:
await ctx.reply('cannot stop server: {} Player(s) online'.format(player_count))
await ctx.reply(f'cannot stop server: {player_count} Player(s) online')
return

if stop_server(server_id):
Expand Down Expand Up @@ -224,8 +194,11 @@ async def bot_help(interaction: discord.Interaction):
# command not found
@bot.event
async def on_command_error(ctx, error):
"""
Handle command not found errors
"""
if isinstance(error, commands.CommandNotFound):
await ctx.send('Command not found. Use `>bot_help` to get a list of available commands.')


bot.run(os.environ['DISCORD_TOKEN'])
bot.run(os.environ['DISCORD_TOKEN'])
Loading

0 comments on commit a740d29

Please sign in to comment.