Skip to content

Commit

Permalink
upgraded to a general minecraft server monitor program; added setting…
Browse files Browse the repository at this point in the history
…s.json; updated readme; updated .gitignore
  • Loading branch information
9FS committed Jan 13, 2024
1 parent e89b4ae commit 9fe0b7e
Show file tree
Hide file tree
Showing 10 changed files with 460 additions and 427 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
.hypothesis/
.pytest_cache/
doc_templates/
config/
log/

Dockerfile
Expand Down
552 changes: 280 additions & 272 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ license = "MIT"
name = "x"
readme = "readme.md"
repository = "https://github.com/9-FS/2023-05-07-Minecraft-Server-Status-for-Discord"
version = "1.1.2"
version = "2.0.0"

[tool.poetry.dependencies]
aiohttp = "^3.8.5"
Expand Down
23 changes: 11 additions & 12 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,31 @@ Author: "구FS"

- [1. General](#1-general)
- [2. How to Install](#2-how-to-install)
- [3. Planned Features](#3-planned-features)

## 1. General

This bot loads a minecraft server IP from `minecraft_server_ip.config`, connects to the discord bot with token `discord_bot.token`, and displays the current status of the specified minecraft server via its rich presence title and bot status. It states either:
- offline, current global IP
- current and maximum possible players online, a player name list should there be any online, current global IP
This discord bot queries the minecraft server specified in "./config/settings.json" `minecraft_server_ip`:`minecraft_server_port` and displays the current status of the minecraft server via its rich presence title and bot status. It states either:
- offline, current IP or domain
- current and maximum possible players online, a player name list should there be any online, current IP or domain

By default, it just displays the given IP or domain. If `convert_to_ip_global` is set to `true`, it will convert the given domain or IP to a public IP for its display. This is useful if you are hosting your server on a local machine and want to display the public IP of your router. Be advised that the given IP or domain in `minecraft_server_ip` will still be used for the query. If you convert the given IP or domain to a public IP, you can choose the preferred IP version with `ip_public_version`. This version is not guaranteed though and will fallback to another version if the preferred version is not available with "https://{ip_public_version}.ident.me/". `minecraft_server_port` can be used to manually specify the port, but it can also be left empty and will default to 25565.

You can specify a bot channel with `discord_bot_channel_name`. If you write "ip" into that channel, the bot will answer with the current display IP or domain for easy copy and paste.

## 2. How to Install

As far as I know, there is currently no way of having rich presences on a per server basis. That's why you can not use this bot by just inviting it to your server. To use this bot:

1. Download the source code or download a release `Minecraft Server Status for Discord.exe`.
1. Copy your discord bot token into `discord_bot.token`.
1. Copy your discord bot token into "./config/settings.json" `discord_bot_token`.
1. Create a discord application [here](https://discord.com/developers/applications).
1. Create your bot.
1. Add it to your server.
1. Copy your token into `discord_bot.token`.

If you don't know how to do these steps, I recommend [this tutorial](https://www.writebots.com/discord-bot-token/).
1. Copy your minecraft server IP with port into `minecraft_server_ip.config`.
1. Execute `main_outer.py` with python or execute the compiled `Metric METAR for Discord.exe`.
1. Copy your minecraft server IP or domain into "./config/settings.json" `minecraft_server_ip`.
1. Execute `main_outer.py` with python or execute the compiled program.

<div style="page-break-after: always;"></div>
</body>

## 3. Planned Features

I want to move the current IP and player list into a separate, multiline text field. I think `discord.Activites.details` should be what I'm looking for, but I can't make it work for some reason. Any help would be greatly appreciated!
</body>
Binary file modified readme.pdf
Binary file not shown.
19 changes: 9 additions & 10 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
aiohttp==3.8.6 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
aiohttp==3.9.1 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
aiosignal==1.3.1 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
async-timeout==4.0.3 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
asyncio-dgram==2.1.2 ; python_full_version >= "3.11.0" and python_version < "4"
attrs==23.1.0 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
certifi==2023.7.22 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
attrs==23.2.0 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
certifi==2023.11.17 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
charset-normalizer==3.3.2 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
colorama==0.4.6 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
discord-py==2.3.2 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
discord==2.3.2 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
dnspython==2.4.2 ; python_full_version >= "3.11.0" and python_version < "4.0"
frozenlist==1.4.0 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
idna==3.4 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
kfsconfig==1.0.2 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
frozenlist==1.4.1 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
idna==3.6 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
kfsconfig==1.1.0 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
kfsfstr==1.1.0 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
kfslog==1.0.1 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
kfsmath==1.0.1 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
mcstatus==11.0.1 ; python_full_version >= "3.11.0" and python_version < "4"
mcstatus==11.1.1 ; python_full_version >= "3.11.0" and python_version < "4"
multidict==6.0.4 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
requests==2.31.0 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
urllib3==2.0.7 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
yarl==1.9.2 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
urllib3==2.1.0 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
yarl==1.9.4 ; python_full_version >= "3.11.0" and python_full_version < "4.0.0"
61 changes: 0 additions & 61 deletions src/convert_to_ip_global.py

This file was deleted.

83 changes: 83 additions & 0 deletions src/convert_to_ip_public.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Copyright (c) 2023 구FS, all rights reserved. Subject to the MIT licence in `licence.md`.
import inspect
import ipaddress
import logging
import requests
import socket


def convert_to_ip_public(ip_or_domain: str, ip_public_version: int=4) -> str:
"""
Converts given IP or domain, which may me local or public, to a guaranteed public IP using "https://ident.me/".
Arguments:
- ip_or_domain: IP given to convert
- ip_public_version: try to convert to IPv4 or IPv6, if fails will fallback to other version, if fails again try any version, if fails again raises ValueError
Returns:
- ip_public: converted IP, guaranteed public
Raises:
- ValueError: IP or domain cannot be converted to IP object.
- TimeoutError: Converting given IP or domain to public IP via "https://ident.me/" timed out.
"""

ip: ipaddress.IPv4Address|ipaddress.IPv6Address # ip which may be local or public
ip_public: ipaddress.IPv4Address|ipaddress.IPv6Address # ip that is guaranteed public, result
TIMEOUT: int=50 # internet connection timeout


if ip_public_version not in (4, 6):
logging.error(f"ip_public_version ({ip_public_version}) must be 4 or 6.")
raise ValueError(f"Error in {convert_to_ip_public.__name__}{inspect.signature(convert_to_ip_public)}: ip_public_version ({ip_public_version}) must be 4 or 6.")


logging.info(f"Converting given IP or domain \"{ip_or_domain}\" to IP...")
try:
ip=ipaddress.ip_address(socket.gethostbyname(ip_or_domain)) # convert IP or domain to IP, construct IP object
except socket.gaierror:
logging.error(f"\rConverting given IP or domain \"{ip_or_domain}\" to IP failed. Unable to get IP address information. Check the given domain/IP and the internet connection.")
raise ValueError(f"Error in {convert_to_ip_public.__name__}{inspect.signature(convert_to_ip_public)}: Converting given IP or domain \"{ip_or_domain}\" to IP failed. Unable to get IP address information. Check the given domain/IP and the internet connection.")
logging.info(f"\rConverted given IP or domain \"{ip_or_domain}\" to IP \"{ip.exploded.upper()}\".")


for _ in range(2): # try to convert to IPv4 or IPv6, if fails will fallback to other version
logging.info(f"Converting given IP \"{ip.exploded.upper()}\" to public IPv{ip_public_version}...")
try:
ip_public=ipaddress.ip_address(requests.get(f"https://{ip_public_version}.ident.me/", timeout=TIMEOUT).text) # try to convert
except TimeoutError:
logging.error(f"\rConverting given IP \"{ip.exploded.upper()}\" to public IPv{ip_public_version} timed out.")
ip_public_version=6
except ValueError as e:
logging.error(f"\rConverting given IP \"{ip.exploded.upper()}\" to public IPv{ip_public_version} failed with ValueError. Network does not seem to have an IPv{ip_public_version}. Error message:\n{e.args}")
match ip_public_version: # fallback to other version
case 4:
ip_public_version=6
case 6:
ip_public_version=4
case _:
logging.critical(f"ip_public_version ({ip_public_version}) is neither 4 nor 6 despite having checked earlier.")
raise RuntimeError(f"Error in {convert_to_ip_public.__name__}{inspect.signature(convert_to_ip_public)}: ip_public_version ({ip_public_version}) is neither 4 nor 6 despite having checked earlier.")
else:
logging.info(f"\rConverted given IP \"{ip.exploded.upper()}\" to public IPv{ip_public_version} \"{ip_public.exploded.upper()}\".")
match ip_public_version: # specific formatting depending on version
case 4:
return ip_public.exploded.upper()
case 6:
return f"[{ip_public.exploded.upper()}]"
case _:
logging.critical(f"ip_public_version ({ip_public_version}) is neither 4 nor 6 despite having checked earlier.")
raise RuntimeError(f"Error in {convert_to_ip_public.__name__}{inspect.signature(convert_to_ip_public)}: ip_public_version ({ip_public_version}) is neither 4 nor 6 despite having checked earlier.")

logging.info(f"Converting given IP \"{ip.exploded.upper()}\" to public IP...") # last ditch effort, try any version
try:
ip_public=ipaddress.ip_address(requests.get("https://ident.me/", timeout=TIMEOUT).text) # convert to public IP, don't care about IPv4 or IPv6
except TimeoutError:
logging.error(f"\rConverting given IP \"{ip.exploded.upper()}\" to public IP timed out.")
raise
except ValueError as e:
logging.error(f"\rConverting given IP \"{ip.exploded.upper()}\" to public IP failed with ValueError. Network does not seem to have an IP. Error message:\n{e.args}")
raise
else:
logging.info(f"\rConverted given IP \"{ip.exploded.upper()}\" to public IP \"{ip_public.exploded.upper()}\".")
return ip_public.exploded.upper()
Loading

0 comments on commit 9fe0b7e

Please sign in to comment.