diff --git a/.gitignore b/.gitignore index 0d950e3..06efbe0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ */__pycache__/ */data/ +*.pyc +.DS_Store diff --git a/bot/.dockerignore b/bot/.dockerignore new file mode 100644 index 0000000..2aed3fe --- /dev/null +++ b/bot/.dockerignore @@ -0,0 +1,6 @@ +env/ +__pycache__ +test.py +Dockerfile +compose.yml +data/ diff --git a/bot/.env b/bot/.env new file mode 100644 index 0000000..706b0f3 --- /dev/null +++ b/bot/.env @@ -0,0 +1,7 @@ +# Discord Stuff +TOKEN = MTI1MDc1MjY1OTY1NzQ1NzY3NA.G9NOoM.6oIosKzw35aJz3QZZ-dkbqDpIO3JxvOeropvqk + +# RCON +RCON_SERVER = 100.80.35.55 +RCON_PASS = garde-studios +RCON_PORT = 31066 diff --git a/bot/Dockerfile b/bot/Dockerfile new file mode 100644 index 0000000..c21f683 --- /dev/null +++ b/bot/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.12.4 + +ADD requirements.txt . +RUN pip install --upgrade pip +RUN pip install -r requirements.txt +ADD . . + +CMD ["python", "main.py"] diff --git a/bot/bot.py b/bot/bot.py new file mode 100644 index 0000000..c929153 --- /dev/null +++ b/bot/bot.py @@ -0,0 +1,35 @@ +import discord +from discord.ext import commands + +from cogs.minecraft import Minecraft +from cogs.user_management import UserManager + +# Setup Environment +import os +from dotenv import load_dotenv +load_dotenv() + +# Discord Stuff +TOKEN = os.environ['TOKEN'] + +# Server Stuff +RCON_SERVER = os.environ['RCON_SERVER'] +RCON_PASS = os.environ['RCON_PASS'] +RCON_PORT = int(os.environ['RCON_PORT']) + +# Setup Basic Permission +intents = discord.Intents.default() +intents.message_content = True + +# Define the actual Bot +bot = commands.Bot(command_prefix='-', intents=intents) + +async def setup(): + await bot.add_cog(Minecraft(bot, RCON_SERVER, RCON_PASS, RCON_PORT)) + await bot.add_cog(UserManager(bot)) + +@bot.event +async def on_ready(): + await setup() + +bot.run(TOKEN) diff --git a/bot/cogs/minecraft.py b/bot/cogs/minecraft.py new file mode 100644 index 0000000..db10c72 --- /dev/null +++ b/bot/cogs/minecraft.py @@ -0,0 +1,185 @@ +import discord +from discord.ext import commands +from mcrcon import MCRcon +import enum +from statemachine import StateMachine, State +from statemachine.states import States + +class BorderWarsSession(StateMachine): + "A workflow maschine for managing InGame States" + Nothing = State(initial=True) + Initialization = State() + Safe = State() + Fight = State() + SuddenDeath = State() + End = State() + + init_game = Nothing.to(Initialization) + start_game = Initialization.to(Safe) + start_fight = Safe.to(Fight) + start_last_round = Fight.to(SuddenDeath) + end_game = Fight.to(End) | SuddenDeath.to(End) + + abort = Initialization.to(Nothing) | Safe.to(Nothing) | Fight.to(Nothing) | SuddenDeath.to(Nothing) | End.to(Nothing) | Nothing.to(Nothing) + reset = End.to(Initialization) + + @Initialization.enter + def initialization(self) -> list: + + + @Safe.enter + def safe(self) -> list: + # Chat countdown + return [ + '''/title @a subtitle ["",{"text":"bei ","color":"blue"},{"text":"BORDER WARS!","bold":true,"color":"red"}]''', + '''/title @a title {"text":"Viel Glück!","bold":true,"color":"blue"}''', + "playsound minecraft:entity.wither.spawn ambient @a 0 64 080", + "/worldborder set 1000", + "/gamerule keepInventory true" + ] + + @Fight.enter + def fight(self) -> list: + # Timer Starten + return [ + '''/title @a subtitle {"text":"ÜBERLEBEN!","bold":true,"color":"red"}''', + '''/title @a title {"text":"Möge der beste","color":"blue"}''', + "/playsound minecraft:item.totem.use ambient @a 0 64 0 80", + "/worldborder set 75 3600", + "/gamerule keepInventory false" + ] + + @SuddenDeath.enter + def death(self) -> list: + # Timer Starten + return [ + '''/title @a title ["",{"text":"Sudden ","color":"dark_blue"},{"text":"DEATH!","bold":true,"color":"red"}]''', + "/playsound minecraft:entity.ender_dragon.growl ambient @a 0 64 0 80", + "/worldborder set 5 600" + ] + + @End.enter + def end(self, playername: str) -> list: + return [ + "/worldborder center 0 0", + "/worldborder set 75", + "/gamerule keepInventory true", + '''/title @a subtitle ["",{"text":"''' + playername + '''","bold":true,"color":"red"},{"text":" gewinnt","color":"dark_blue"}]''', + '''/title @a title {"text":"ENDE!","color":"dark_blue"}''', + "/playsound minecraft:entity.ender_dragon_death ambient @a 0 64 0 80", + ] + + +class Whitelist(StateMachine): + "A workflow machine for managing Whitelist states" + On = State(initial=True) + Off = State() + toggle = On.to(Off) | Off.to(On) + +class RCON(MCRcon): + def __init__(self, ip: str, secret: str, port: int = 31066): + super().__init__(ip, secret, port) + self.connect() + + def whitelist(self): + self.whitelist.toggle() + cmds = "/whitelist {}".format(self.whitelist.current_state.id) + print(cmds) + + def _sendcmd(self, cmds: str | list) -> None: + if isinstance(cmds, str): + return self.server.command(str) + if isinstance(cmds, list): + return [self.server.commands(cmd) for cmd in cmds] + + def __del__(self): + self.disconnect() + +class Minecraft(commands.Cog): + def __init__(self, bot: commands.Bot, ip: str, secret: str, port: int = 31066): + self.bot = bot + self.server = RCON(ip, secret, port) + self.session = BorderWarsSession() + self.whitelist = Whitelist() + + @commands.hybrid_command(name='whitelist') + async def whitelist(self, ctx: commands.Context): + """ + Toggles Servers Whitelist + + Parameters + ---------- + ctx: commands.Context + The context of the command invocation + """ + await self.whitelist.activate_initial_state() + await ctx.send("Whitelist") + + @commands.hybrid_command(name='start') + async def start(self, ctx: commands.Context): + """ + Starts a Border Wars Session + + Parameters + ---------- + ctx: commands.Context + The context of the command invocation + """ + cmds = + await ctx.send("Start") + + @commands.hybrid_command(name='init') + async def init(self, ctx: commands.Context): + """ + Initialize a Border Wars session + + Parameters + ---------- + ctx: commands.Context + The context of the command invocation + """ + cmds = [ + "/worldborder center 0 0", + "/worldborder set 5", + "/whitelist off" + ] + await self.session.activate_initial_state() + await self.session.init_game() + await ctx.send(self.session.current_state) + + @commands.hybrid_command(name='end') + async def end(self, ctx: commands.Context): + """ + Ends a Border Wars session + + Parameters + ---------- + ctx: commands.Context + The context of the command invocation + """ + await ctx.send("End") + + @commands.hybrid_command(name='rules') + async def rules(self, ctx: commands.Context): + """ + Displays the Border Wars rules + + Parameters + ---------- + ctx: commands.Context + The context of the command invocation + """ + await ctx.send("Rules") + + @commands.hybrid_command(name='custom') + async def custom(self, ctx: commands.Context): + """ + Register a custom command + + Parameters + ---------- + ctx: commands.Context + The context of the command invocation + """ + await ctx.send("Custom") + diff --git a/bot/cogs/user_management.py b/bot/cogs/user_management.py new file mode 100644 index 0000000..5283d20 --- /dev/null +++ b/bot/cogs/user_management.py @@ -0,0 +1,12 @@ +from discord.ext import commands +from sqlalchemy import create_engine +from sqlalchemy.orm import DeclarativeBase + +#engine = create_engine("sqlite://user.sqlite", echo=True) +#connection = engine.connect() + +class User(DeclarativeBase): + pass + +class UserManager(commands.Cog): + pass diff --git a/bot/compose.yml b/bot/compose.yml new file mode 100644 index 0000000..b9b088e --- /dev/null +++ b/bot/compose.yml @@ -0,0 +1,16 @@ +services: + bot: + build: + context: . + dockerfile: Dockerfile + # volumes: + # - bot_data:/home + + +volumes: + bot_data: + driver: local + driver_opts: + type: none + device: ./data + o: bind diff --git a/bot/requirements.txt b/bot/requirements.txt new file mode 100644 index 0000000..c7b829a --- /dev/null +++ b/bot/requirements.txt @@ -0,0 +1,14 @@ +aiohttp==3.9.5 +aiosignal==1.3.1 +attrs==23.2.0 +discord.py==2.4.0 +frozenlist==1.4.1 +greenlet==3.0.3 +idna==3.7 +mcrcon==0.7.0 +multidict==6.0.5 +python-dotenv==1.0.1 +python-statemachine==2.3.1 +SQLAlchemy==2.0.31 +typing_extensions==4.12.2 +yarl==1.9.4 diff --git a/bot/test.py b/bot/test.py new file mode 100644 index 0000000..257cd65 --- /dev/null +++ b/bot/test.py @@ -0,0 +1,8 @@ +import enum + + + #off = states.On.to(states.Off) + #on = states.Off.to(states.On) + +if __name__ == "__main__": + WhitelistMachine()._graph().write_png("./whitelist.png") diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..bad5fcc --- /dev/null +++ b/compose.yml @@ -0,0 +1,5 @@ +include: + - ./bot/compose.yml + - ./postgres/compose.yml + # - ./pgadmin4/compose.yml + diff --git a/pgadmin4/.env b/pgadmin4/.env new file mode 100644 index 0000000..94bde4d --- /dev/null +++ b/pgadmin4/.env @@ -0,0 +1,3 @@ +PGADMIN_DEFAULT_EMAIL=p.keier@beyerstedt-it.de +PGADMIN_DEFAULT_PASSWORD=1P2h3i4lon +PGADMIN_DISABLE_POSTFIX=true diff --git a/pgadmin4/compose.yml b/pgadmin4/compose.yml new file mode 100644 index 0000000..0336a8c --- /dev/null +++ b/pgadmin4/compose.yml @@ -0,0 +1,19 @@ +services: + pgadmin: + image: dpage/pgadmin4 + container_name: pg4 + restart: always + env_file: ./.env + ports: + - "8888:80" + volumes: + - ./server.json:/var/lib/pgadmin/server.json + - pg4_data:/var/lib/pgadmin + +volumes: + pg4_data: + driver: local + driver_opts: + type: none + device: ./data + o: bind diff --git a/pgadmin4/server.json b/pgadmin4/server.json new file mode 100644 index 0000000..b2cb579 --- /dev/null +++ b/pgadmin4/server.json @@ -0,0 +1,13 @@ +{ + "Servers": { + "1": { + "Name": "Bot", + "Group": "Local", + "Port": 5432, + "Username": "garde-studios", + "Host": "localhost", + "SSLMode": "prefer", + "MaintenanceDB": "postgres" + } + } +} diff --git a/postgres/.env b/postgres/.env new file mode 100644 index 0000000..f643c22 --- /dev/null +++ b/postgres/.env @@ -0,0 +1,2 @@ +POSTGRES_USER=garde-studios +POSTGRES_PASSWORD=garde-studios diff --git a/postgres/compose.yml b/postgres/compose.yml new file mode 100644 index 0000000..0819cb9 --- /dev/null +++ b/postgres/compose.yml @@ -0,0 +1,18 @@ +services: + db: + image: postgres + container_name: pgdb + restart: always + env_file: ./.env + ports: + - "5432:5432" + volumes: + - db_data:/var/lib/postgresql/data + +volumes: + db_data: + driver: local + driver_opts: + type: none + device: ./data + o: bind